tests/fuzzers: move fuzzers into native packages (#28467)

This commit is contained in:
Daniel Liu 2024-12-19 11:57:47 +08:00
parent 441f8ed70c
commit 4cc2b2ea5f
8 changed files with 275 additions and 247 deletions

View file

@ -18,6 +18,7 @@ package bitutil
import ( import (
"bytes" "bytes"
"fmt"
"math/rand" "math/rand"
"testing" "testing"
@ -48,19 +49,23 @@ 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)
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if err != nil {
t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
continue
}
if !bytes.Equal(data, proc) {
t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
} }
} }
} }
func testEncodingCycle(data []byte) error {
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if err != nil {
return fmt.Errorf("failed to decompress compressed data: %v", err)
}
if !bytes.Equal(data, proc) {
return fmt.Errorf("compress/decompress mismatch: have %x, want %x", proc, data)
}
return nil
}
// Tests that data bitset decoding and rencoding works and is bijective. // Tests that data bitset decoding and rencoding works and is bijective.
func TestDecodingCycle(t *testing.T) { func TestDecodingCycle(t *testing.T) {
tests := []struct { tests := []struct {
@ -179,3 +184,41 @@ 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")
}
}

View 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/XinFinOrg/XDPoSChain/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)
}
}
}

View file

@ -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/XinFinOrg/XDPoSChain/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(nil, p, input, gas)
if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a)
}
}) })
} }

View file

@ -18,13 +18,11 @@ package runtime
import ( import (
"testing" "testing"
"github.com/XinFinOrg/XDPoSChain/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,
}) })
}) })

View file

@ -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/XinFinOrg/XDPoSChain/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")
}
}

View file

@ -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/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/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
}

View file

@ -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
} }

View file

@ -21,6 +21,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io"
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
@ -346,8 +347,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.
@ -373,36 +375,53 @@ 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: case opGet, opDelete:
step.key = genKey() step.key = genKey()
} }
steps = append(steps, step) steps = append(steps, step)
} }
return reflect.ValueOf(steps) return steps
} }
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 {
triedb := NewDatabase(memorydb.New()) triedb := NewDatabase(memorydb.New())
tr, _ := New(common.Hash{}, triedb) tr, _ := New(common.Hash{}, triedb)
@ -432,12 +451,12 @@ func runRandTest(rt randTest) bool {
hash, err := tr.Commit(nil) hash, err := tr.Commit(nil)
if err != nil { if err != nil {
rt[i].err = err rt[i].err = err
return false return err
} }
newtr, err := New(hash, triedb) newtr, err := New(hash, triedb)
if err != nil { if err != nil {
rt[i].err = err rt[i].err = err
return false return err
} }
tr = newtr tr = newtr
case opItercheckhash: case opItercheckhash:
@ -452,14 +471,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))
} }
@ -854,3 +873,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)
}
})
}