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"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
fuzz "github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
||||
// Just replace testString with the data in .quoted
|
||||
func TestReplicate(t *testing.T) {
|
||||
testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
|
||||
data := []byte(testString)
|
||||
fuzzAbi(data)
|
||||
//t.Skip("Test only useful for reproducing issues")
|
||||
fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
|
||||
//fuzzAbi([]byte("asdfasdfkadsf;lasdf;lasd;lfk"))
|
||||
}
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
// FuzzABI is the main entrypoint for fuzzing
|
||||
func FuzzABI(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzzAbi(data)
|
||||
})
|
||||
@ -42,10 +42,8 @@ func Fuzz(f *testing.F) {
|
||||
|
||||
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]}
|
||||
stateMut = []string{"pure", "view", "payable"}
|
||||
pays = []string{"true", "false"}
|
||||
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||
varNames = append(vNames, names...)
|
||||
varTypes = []string{"bool", "address", "bytes", "string",
|
||||
@ -62,7 +60,7 @@ var (
|
||||
"bytes32", "bytes"}
|
||||
)
|
||||
|
||||
func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
|
||||
func unpackPack(abi ABI, method string, input []byte) ([]interface{}, bool) {
|
||||
if out, err := abi.Unpack(method, input); err == nil {
|
||||
_, err := abi.Pack(method, out...)
|
||||
if err != nil {
|
||||
@ -78,7 +76,7 @@ func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
||||
func packUnpack(abi ABI, method string, input *[]interface{}) bool {
|
||||
if packed, err := abi.Pack(method, input); err == nil {
|
||||
outptr := reflect.New(reflect.TypeOf(input))
|
||||
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
||||
@ -94,12 +92,12 @@ func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type args struct {
|
||||
type arg struct {
|
||||
name string
|
||||
typ string
|
||||
}
|
||||
|
||||
func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
|
||||
func createABI(name string, stateMutability, payable *string, inputs []arg) (ABI, error) {
|
||||
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
||||
if stateMutability != nil {
|
||||
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
||||
@ -126,56 +124,55 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
|
||||
sig += "} ]"
|
||||
}
|
||||
sig += `}]`
|
||||
|
||||
return abi.JSON(strings.NewReader(sig))
|
||||
//fmt.Printf("sig: %s\n", sig)
|
||||
return JSON(strings.NewReader(sig))
|
||||
}
|
||||
|
||||
func fuzzAbi(input []byte) int {
|
||||
good := false
|
||||
fuzzer := fuzz.NewFromGoFuzz(input)
|
||||
|
||||
name := names[getUInt(fuzzer)%len(names)]
|
||||
stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
|
||||
payable := payables[getUInt(fuzzer)%len(payables)]
|
||||
maxLen := 5
|
||||
for k := 1; k < maxLen; k++ {
|
||||
var arg []args
|
||||
for i := k; i > 0; i-- {
|
||||
argName := varNames[i]
|
||||
argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
|
||||
if getUInt(fuzzer)%10 == 0 {
|
||||
func fuzzAbi(input []byte) {
|
||||
var (
|
||||
fuzzer = fuzz.NewFromGoFuzz(input)
|
||||
name = oneOf(fuzzer, names)
|
||||
stateM = oneOfOrNil(fuzzer, stateMut)
|
||||
payable = oneOfOrNil(fuzzer, pays)
|
||||
arguments []arg
|
||||
)
|
||||
for i := 0; i < upTo(fuzzer, 10); i++ {
|
||||
argName := oneOf(fuzzer, varNames)
|
||||
argTyp := oneOf(fuzzer, varTypes)
|
||||
switch upTo(fuzzer, 10) {
|
||||
case 0: // 10% chance to make it a slice
|
||||
argTyp += "[]"
|
||||
} else if getUInt(fuzzer)%10 == 0 {
|
||||
arrayArgs := getUInt(fuzzer)%30 + 1
|
||||
argTyp += fmt.Sprintf("[%d]", arrayArgs)
|
||||
case 1: // 10% chance to make it an array
|
||||
argTyp += fmt.Sprintf("[%d]", 1+upTo(fuzzer, 30))
|
||||
default:
|
||||
}
|
||||
arg = append(arg, args{
|
||||
name: argName,
|
||||
typ: argTyp,
|
||||
})
|
||||
arguments = append(arguments, arg{name: argName, typ: argTyp})
|
||||
}
|
||||
abi, err := createABI(name, stateM, payable, arg)
|
||||
abi, err := createABI(name, stateM, payable, arguments)
|
||||
if err != nil {
|
||||
continue
|
||||
//fmt.Printf("err: %v\n", err)
|
||||
panic(err)
|
||||
}
|
||||
structs, b := unpackPack(abi, name, input)
|
||||
c := packUnpack(abi, name, &structs)
|
||||
good = good || b || c
|
||||
}
|
||||
if good {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
structs, _ := unpackPack(abi, name, input)
|
||||
_ = packUnpack(abi, name, &structs)
|
||||
}
|
||||
|
||||
func getUInt(fuzzer *fuzz.Fuzzer) int {
|
||||
func upTo(fuzzer *fuzz.Fuzzer, max int) int {
|
||||
var i int
|
||||
fuzzer.Fuzz(&i)
|
||||
if i < 0 {
|
||||
i = -i
|
||||
if i < 0 {
|
||||
return 0
|
||||
return (-1 - i) % max
|
||||
}
|
||||
return i % 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
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
func FuzzPassword(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, password string) {
|
||||
ks := NewKeyStore(t.TempDir(), LightScryptN, LightScryptP)
|
||||
a, err := ks.NewAccount(password)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ks.Unlock(a, password); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
@ -18,6 +18,7 @@ package bitutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
@ -48,17 +49,21 @@ func TestEncodingCycle(t *testing.T) {
|
||||
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
|
||||
}
|
||||
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))
|
||||
if err != nil {
|
||||
t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
|
||||
continue
|
||||
return fmt.Errorf("failed to decompress compressed data: %v", err)
|
||||
}
|
||||
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.
|
||||
@ -179,3 +184,40 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
|
||||
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzEncoder(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
if err := testEncodingCycle(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzzDecode(data)
|
||||
})
|
||||
}
|
||||
|
||||
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
|
||||
// reencoding algorithm.
|
||||
func fuzzDecode(data []byte) {
|
||||
blob, err := DecompressBytes(data, 1024)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// re-compress it (it's OK if the re-compressed differs from the
|
||||
// original - the first input may not have been compressed at all)
|
||||
comp := CompressBytes(blob)
|
||||
if len(comp) > len(blob) {
|
||||
// After compression, it must be smaller or equal
|
||||
panic("bad compression")
|
||||
}
|
||||
// But decompressing it once again should work
|
||||
decomp, err := DecompressBytes(data, 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !bytes.Equal(decomp, blob) {
|
||||
panic("content mismatch")
|
||||
}
|
||||
}
|
||||
|
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
|
||||
// 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) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func FuzzPrecompiledContracts(f *testing.F) {
|
||||
// Create list of addresses
|
||||
var addrs []common.Address
|
||||
for k := range allPrecompiles {
|
||||
addrs = append(addrs, k)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, addr uint8, input []byte) {
|
||||
a := addrs[int(addr)%len(addrs)]
|
||||
p := allPrecompiles[a]
|
||||
gas := p.RequiredGas(input)
|
||||
if gas > 10_000_000 {
|
||||
return
|
||||
}
|
||||
inWant := string(input)
|
||||
RunPrecompiledContract(p, input, gas)
|
||||
if inHave := string(input); inWant != inHave {
|
||||
t.Errorf("Precompiled %v modified input data", a)
|
||||
}
|
||||
})
|
||||
}
|
@ -18,13 +18,11 @@ package runtime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||
)
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
func FuzzVmRuntime(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, code, input []byte) {
|
||||
runtime.Execute(code, input, &runtime.Config{
|
||||
Execute(code, input, &Config{
|
||||
GasLimit: 12000000,
|
||||
})
|
||||
})
|
@ -21,6 +21,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -28,7 +29,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@ -36,6 +36,56 @@ import (
|
||||
fuzz "github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
func FuzzARange(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &GetAccountRangePacket{}, GetAccountRangeMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzSRange(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &GetStorageRangesPacket{}, GetStorageRangesMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzByteCodes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &GetByteCodesPacket{}, GetByteCodesMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzTrieNodes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &GetTrieNodesPacket{}, GetTrieNodesMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func doFuzz(input []byte, obj interface{}, code int) {
|
||||
bc := getChain()
|
||||
defer bc.Stop()
|
||||
fuzz.NewFromGoFuzz(input).Fuzz(obj)
|
||||
var data []byte
|
||||
switch p := obj.(type) {
|
||||
case *GetTrieNodesPacket:
|
||||
p.Root = trieRoot
|
||||
data, _ = rlp.EncodeToBytes(obj)
|
||||
default:
|
||||
data, _ = rlp.EncodeToBytes(obj)
|
||||
}
|
||||
cli := &dummyRW{
|
||||
code: uint64(code),
|
||||
data: data,
|
||||
}
|
||||
peer := NewFakePeer(65, "gazonk01", cli)
|
||||
err := HandleMessage(&dummyBackend{bc}, peer)
|
||||
switch {
|
||||
case err == nil && cli.writeCount != 1:
|
||||
panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
|
||||
case err != nil && cli.writeCount != 0:
|
||||
panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
|
||||
}
|
||||
}
|
||||
|
||||
var trieRoot common.Hash
|
||||
|
||||
func getChain() *core.BlockChain {
|
||||
@ -87,9 +137,9 @@ type dummyBackend struct {
|
||||
}
|
||||
|
||||
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) Handle(*snap.Peer, snap.Packet) error { return nil }
|
||||
func (d *dummyBackend) Handle(*Peer, Packet) error { return nil }
|
||||
|
||||
type dummyRW struct {
|
||||
code uint64
|
||||
@ -110,34 +160,3 @@ func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
|
||||
d.writeCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
func doFuzz(input []byte, obj interface{}, code int) int {
|
||||
if len(input) > 1024*4 {
|
||||
return -1
|
||||
}
|
||||
bc := getChain()
|
||||
defer bc.Stop()
|
||||
backend := &dummyBackend{bc}
|
||||
fuzz.NewFromGoFuzz(input).Fuzz(obj)
|
||||
var data []byte
|
||||
switch p := obj.(type) {
|
||||
case *snap.GetTrieNodesPacket:
|
||||
p.Root = trieRoot
|
||||
data, _ = rlp.EncodeToBytes(obj)
|
||||
default:
|
||||
data, _ = rlp.EncodeToBytes(obj)
|
||||
}
|
||||
cli := &dummyRW{
|
||||
code: uint64(code),
|
||||
data: data,
|
||||
}
|
||||
peer := snap.NewFakePeer(65, "gazonk01", cli)
|
||||
err := snap.HandleMessage(backend, peer)
|
||||
switch {
|
||||
case err == nil && cli.writeCount != 1:
|
||||
panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
|
||||
case err != nil && cli.writeCount != 0:
|
||||
panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
|
||||
}
|
||||
return 1
|
||||
}
|
2
go.mod
2
go.mod
@ -33,7 +33,7 @@ require (
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/graph-gophers/graphql-go v1.3.0
|
||||
|
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/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
200
oss-fuzz.sh
200
oss-fuzz.sh
@ -48,38 +48,26 @@ DOG
|
||||
cd -
|
||||
}
|
||||
|
||||
function build_native_go_fuzzer() {
|
||||
fuzzer=$1
|
||||
function=$2
|
||||
path=$3
|
||||
tags="-tags gofuzz"
|
||||
|
||||
if [[ $SANITIZER == *coverage* ]]; then
|
||||
coverbuild $path $function $fuzzer $coverpkg
|
||||
else
|
||||
go-118-fuzz-build $tags -o $fuzzer.a -func $function $path
|
||||
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
|
||||
fi
|
||||
}
|
||||
|
||||
function compile_fuzzer() {
|
||||
path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
|
||||
package=$1
|
||||
function=$2
|
||||
fuzzer=$3
|
||||
file=$4
|
||||
|
||||
path=$GOPATH/src/$package
|
||||
|
||||
echo "Building $fuzzer"
|
||||
cd $path
|
||||
|
||||
# Install build dependencies
|
||||
go install github.com/AdamKorcz/go-118-fuzz-build@latest
|
||||
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
||||
go mod tidy
|
||||
go get github.com/holiman/gofuzz-shim/testing
|
||||
|
||||
# Test if file contains a line with "func $function(" and "testing.F".
|
||||
if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
|
||||
then
|
||||
build_native_go_fuzzer $fuzzer $function $path
|
||||
if [[ $SANITIZER == *coverage* ]]; then
|
||||
coverbuild $path $function $fuzzer $coverpkg
|
||||
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
|
||||
|
||||
## Check if there exists a seed corpus file
|
||||
@ -92,42 +80,140 @@ function compile_fuzzer() {
|
||||
cd -
|
||||
}
|
||||
|
||||
compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
|
||||
compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
|
||||
compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
|
||||
compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
|
||||
compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
|
||||
compile_fuzzer tests/fuzzers/runtime Fuzz fuzzVmRuntime
|
||||
compile_fuzzer tests/fuzzers/keystore Fuzz fuzzKeystore
|
||||
compile_fuzzer tests/fuzzers/txfetcher Fuzz fuzzTxfetcher
|
||||
compile_fuzzer tests/fuzzers/rlp Fuzz fuzzRlp
|
||||
compile_fuzzer tests/fuzzers/trie Fuzz fuzzTrie
|
||||
compile_fuzzer tests/fuzzers/stacktrie Fuzz fuzzStackTrie
|
||||
compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
|
||||
compile_fuzzer tests/fuzzers/abi Fuzz fuzzAbi
|
||||
compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
|
||||
compile_fuzzer tests/fuzzers/secp256k1 Fuzz fuzzSecp256k1
|
||||
compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
|
||||
go install github.com/holiman/gofuzz-shim@latest
|
||||
repo=$GOPATH/src/github.com/ethereum/go-ethereum
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/accounts/abi \
|
||||
FuzzABI fuzzAbi \
|
||||
$repo/accounts/abi/abifuzzer_test.go
|
||||
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG2Add fuzz_g2_add
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG2Mul fuzz_g2_mul
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG2MultiExp fuzz_g2_multiexp
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzPairing fuzz_pairing
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzMapG1 fuzz_map_g1
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzMapG2 fuzz_map_g2
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||
FuzzEncoder fuzzBitutilEncoder \
|
||||
$repo/common/bitutil/compress_test.go
|
||||
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1Add fuzz_cross_g1_add
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1MultiExp fuzz_cross_g1_multiexp
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG2Add fuzz_cross_g2_add
|
||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossPairing fuzz_cross_pairing
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||
FuzzDecoder fuzzBitutilDecoder \
|
||||
$repo/common/bitutil/compress_test.go
|
||||
|
||||
compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
|
||||
compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
|
||||
compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
|
||||
compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/core/vm/runtime \
|
||||
FuzzVmRuntime fuzzVmRuntime\
|
||||
$repo/core/vm/runtime/runtime_fuzz_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/core/vm \
|
||||
FuzzPrecompiledContracts fuzzPrecompiledContracts\
|
||||
$repo/core/vm/contracts_fuzz_test.go,$repo/core/vm/contracts_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/core/types \
|
||||
FuzzRLP fuzzRlp \
|
||||
$repo/core/types/rlp_fuzzer_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/crypto/blake2b \
|
||||
Fuzz fuzzBlake2b \
|
||||
$repo/crypto/blake2b/blake2b_f_fuzz_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/accounts/keystore \
|
||||
FuzzPassword fuzzKeystore \
|
||||
$repo/accounts/keystore/keystore_fuzzing_test.go
|
||||
|
||||
pkg=$repo/trie/
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/trie \
|
||||
FuzzTrie fuzzTrie \
|
||||
$pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/iterator_test.go,$pkg/sync_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/trie \
|
||||
FuzzStackTrie fuzzStackTrie \
|
||||
$pkg/stacktrie_fuzzer_test.go,$pkg/iterator_test.go,$pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/sync_test.go
|
||||
|
||||
#compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
|
||||
FuzzARange fuzz_account_range \
|
||||
$repo/eth/protocols/snap/handler_fuzzing_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
|
||||
FuzzSRange fuzz_storage_range \
|
||||
$repo/eth/protocols/snap/handler_fuzzing_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
|
||||
FuzzByteCodes fuzz_byte_codes \
|
||||
$repo/eth/protocols/snap/handler_fuzzing_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
|
||||
FuzzTrieNodes fuzz_trie_nodes\
|
||||
$repo/eth/protocols/snap/handler_fuzzing_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
|
||||
FuzzAdd fuzzBn256Add\
|
||||
$repo/tests/fuzzers/bn256/bn256_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
|
||||
FuzzMul fuzzBn256Mul \
|
||||
$repo/tests/fuzzers/bn256/bn256_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
|
||||
FuzzPair fuzzBn256Pair \
|
||||
$repo/tests/fuzzers/bn256/bn256_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/txfetcher \
|
||||
Fuzz fuzzTxfetcher \
|
||||
$repo/tests/fuzzers/txfetcher/txfetcher_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG1Add fuzz_g1_add\
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG1Mul fuzz_g1_mul\
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG1MultiExp fuzz_g1_multiexp \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG2Add fuzz_g2_add \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG2Mul fuzz_g2_mul\
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzG2MultiExp fuzz_g2_multiexp \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzPairing fuzz_pairing \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzMapG1 fuzz_map_g1\
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzMapG2 fuzz_map_g2 \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzCrossG1Add fuzz_cross_g1_add \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzCrossG1MultiExp fuzz_cross_g1_multiexp \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzCrossG2Add fuzz_cross_g2_add \
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
|
||||
FuzzCrossPairing fuzz_cross_pairing\
|
||||
$repo/tests/fuzzers/bls12381/bls12381_test.go
|
||||
|
||||
compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \
|
||||
Fuzz fuzzSecp256k1\
|
||||
$repo/tests/fuzzers/secp256k1/secp_test.go
|
||||
|
||||
|
||||
#compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
|
||||
#compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
|
||||
#compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
|
||||
|
||||
#TODO: move this to tests/fuzzers, if possible
|
||||
compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b
|
||||
|
@ -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 (
|
||||
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,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"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
@ -362,7 +363,9 @@ func TestRandomCases(t *testing.T) {
|
||||
{op: 1, key: common.Hex2Bytes("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
|
||||
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
|
||||
}
|
||||
runRandTest(rt)
|
||||
if err := runRandTest(rt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// randTest performs random trie operations.
|
||||
@ -389,33 +392,45 @@ const (
|
||||
)
|
||||
|
||||
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
|
||||
var finishedFn = func() bool {
|
||||
size--
|
||||
return size > 0
|
||||
}
|
||||
return reflect.ValueOf(generateSteps(finishedFn, r))
|
||||
}
|
||||
|
||||
func generateSteps(finished func() bool, r io.Reader) randTest {
|
||||
var allKeys [][]byte
|
||||
var one = []byte{0}
|
||||
genKey := func() []byte {
|
||||
if len(allKeys) < 2 || r.Intn(100) < 10 {
|
||||
r.Read(one)
|
||||
if len(allKeys) < 2 || one[0]%100 > 90 {
|
||||
// new key
|
||||
key := make([]byte, r.Intn(50))
|
||||
size := one[0] % 50
|
||||
key := make([]byte, size)
|
||||
r.Read(key)
|
||||
allKeys = append(allKeys, key)
|
||||
return key
|
||||
}
|
||||
// use existing key
|
||||
return allKeys[r.Intn(len(allKeys))]
|
||||
idx := int(one[0]) % len(allKeys)
|
||||
return allKeys[idx]
|
||||
}
|
||||
|
||||
var steps randTest
|
||||
for i := 0; i < size; i++ {
|
||||
step := randTestStep{op: r.Intn(opMax)}
|
||||
for !finished() {
|
||||
r.Read(one)
|
||||
step := randTestStep{op: int(one[0]) % opMax}
|
||||
switch step.op {
|
||||
case opUpdate:
|
||||
step.key = genKey()
|
||||
step.value = make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(step.value, uint64(i))
|
||||
binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
|
||||
case opGet, opDelete, opProve:
|
||||
step.key = genKey()
|
||||
}
|
||||
steps = append(steps, step)
|
||||
}
|
||||
return reflect.ValueOf(steps)
|
||||
return steps
|
||||
}
|
||||
|
||||
func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
|
||||
@ -460,7 +475,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runRandTest(rt randTest) bool {
|
||||
// runRandTestBool coerces error to boolean, for use in quick.Check
|
||||
func runRandTestBool(rt randTest) bool {
|
||||
return runRandTest(rt) == nil
|
||||
}
|
||||
|
||||
func runRandTest(rt randTest) error {
|
||||
var scheme = rawdb.HashScheme
|
||||
if rand.Intn(2) == 0 {
|
||||
scheme = rawdb.PathScheme
|
||||
@ -513,12 +533,12 @@ func runRandTest(rt randTest) bool {
|
||||
newtr, err := New(TrieID(root), triedb)
|
||||
if err != nil {
|
||||
rt[i].err = err
|
||||
return false
|
||||
return err
|
||||
}
|
||||
if nodes != nil {
|
||||
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
|
||||
rt[i].err = err
|
||||
return false
|
||||
return err
|
||||
}
|
||||
}
|
||||
tr = newtr
|
||||
@ -587,14 +607,14 @@ func runRandTest(rt randTest) bool {
|
||||
}
|
||||
// Abort the test on error.
|
||||
if rt[i].err != nil {
|
||||
return false
|
||||
return rt[i].err
|
||||
}
|
||||
}
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
if err := quick.Check(runRandTest, nil); err != nil {
|
||||
if err := quick.Check(runRandTestBool, nil); err != nil {
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
|
||||
}
|
||||
@ -1185,3 +1205,17 @@ func TestDecodeNode(t *testing.T) {
|
||||
decodeNode(hash, elems)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzTrie(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
var steps = 500
|
||||
var input = bytes.NewReader(data)
|
||||
var finishedFn = func() bool {
|
||||
steps--
|
||||
return steps < 0 || input.Len() == 0
|
||||
}
|
||||
if err := runRandTest(generateSteps(finishedFn, input)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user