mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Move Fuzz/ Into Testing/ (#9617)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
217
testing/fuzz/BUILD.bazel
Normal file
217
testing/fuzz/BUILD.bazel
Normal file
@@ -0,0 +1,217 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("//tools/go:fuzz.bzl", "go_fuzz_test")
|
||||
load("//tools:ssz.bzl", "SSZ_DEPS", "ssz_gen_marshal")
|
||||
load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
|
||||
# gazelle:ignore
|
||||
|
||||
config_setting(
|
||||
name = "fuzzing_enabled",
|
||||
values = {"define": "gotags=libfuzzer"},
|
||||
)
|
||||
|
||||
ssz_gen_marshal(
|
||||
name = "ssz_generated_files",
|
||||
srcs = ["inputs.go"],
|
||||
includes = [
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
objs = [
|
||||
"InputBlockWithPrestate",
|
||||
],
|
||||
)
|
||||
|
||||
IMPORT_PATH = "github.com/prysmaticlabs/prysm/fuzz"
|
||||
|
||||
COMMON_DEPS = [
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
] + SSZ_DEPS
|
||||
|
||||
COMMON_SRCS = [
|
||||
"common.go",
|
||||
"inputs.go",
|
||||
":ssz_generated_files",
|
||||
]
|
||||
|
||||
SRCS = COMMON_SRCS + glob(["*_fuzz.go"])
|
||||
|
||||
test_suite(
|
||||
name = "fuzz_tests",
|
||||
tags = ["manual"],
|
||||
tests = [
|
||||
":block_fuzz_test_with_libfuzzer",
|
||||
":rpc_status_fuzz_test_with_libfuzzer",
|
||||
":state_fuzz_test_with_libfuzzer",
|
||||
],
|
||||
)
|
||||
|
||||
go_fuzz_test(
|
||||
name = "block_fuzz_test",
|
||||
srcs = [
|
||||
"block_fuzz.go",
|
||||
] + COMMON_SRCS,
|
||||
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_block_header",
|
||||
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/block_header",
|
||||
func = "BeaconFuzzBlock",
|
||||
importpath = IMPORT_PATH,
|
||||
max_len = 30000000,
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//testing/fuzz/testing:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
"//beacon-chain/powchain/testing:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_pubsub//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
] + COMMON_DEPS,
|
||||
)
|
||||
|
||||
go_fuzz_test(
|
||||
name = "rpc_status_fuzz_test",
|
||||
srcs = [
|
||||
"rpc_status_fuzz.go",
|
||||
] + COMMON_SRCS,
|
||||
corpus = "rpc_status_corpus",
|
||||
corpus_path = "fuzz/rpc_status_corpus",
|
||||
func = "BeaconFuzzP2PRPCStatus",
|
||||
importpath = IMPORT_PATH,
|
||||
deps = [
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//host:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//mux:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
] + COMMON_DEPS,
|
||||
)
|
||||
|
||||
go_fuzz_test(
|
||||
name = "ssz_encoder_attestations_test",
|
||||
srcs = [
|
||||
"ssz_encoder_attestations_fuzz.go",
|
||||
] + COMMON_SRCS,
|
||||
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_block_header",
|
||||
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/block_header",
|
||||
func = "SszEncoderAttestationFuzz",
|
||||
importpath = IMPORT_PATH,
|
||||
deps = [
|
||||
"//beacon-chain/p2p/encoder:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
] + COMMON_DEPS,
|
||||
)
|
||||
|
||||
go_fuzz_test(
|
||||
name = "state_fuzz_test",
|
||||
srcs = [
|
||||
"state_fuzz.go",
|
||||
] + COMMON_SRCS,
|
||||
corpus = "@sigp_beacon_fuzz_corpora//:0_11_0_mainnet_beaconstate",
|
||||
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/beaconstate",
|
||||
func = "BeaconStateFuzz",
|
||||
importpath = IMPORT_PATH,
|
||||
max_len = 30000000,
|
||||
deps = [
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
] + COMMON_DEPS,
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"common.go",
|
||||
"inputs.go",
|
||||
"rpc_status_fuzz.go",
|
||||
"ssz_encoder_attestations_fuzz.go",
|
||||
"state_fuzz.go",
|
||||
":ssz_generated_files", # keep
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/fuzz",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/p2p/encoder:go_default_library",
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//testing/fuzz/testing:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//host:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_pubsub//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
] + SSZ_DEPS, # keep
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "inputs",
|
||||
srcs = [
|
||||
"inputs.go",
|
||||
":ssz_generated_files", # keep
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/fuzz",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
] + SSZ_DEPS, # keep
|
||||
)
|
||||
102
testing/fuzz/README.md
Normal file
102
testing/fuzz/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Prysm Fuzz Testing
|
||||
|
||||
[](https://app.fuzzit.dev/orgs/prysmaticlabs-gh/dashboard)
|
||||
|
||||
## Adding a fuzz test
|
||||
|
||||
Fuzz testing attempts to find crash level bugs within the tested code paths, but could also be used
|
||||
as a sanity check certain logic.
|
||||
|
||||
### 1) Determining an ideal target
|
||||
|
||||
A fuzz test inputs pseudo-random data to a given method and attempts to find input data that tests
|
||||
as many code branches as possible. When choosing a target to test, consider that the method under
|
||||
test should be as stateless as possible. While stateful methods (i.e. methods that use a cache),
|
||||
can be tested, they are often hard to reproduce in a regression test. Consider disabling any caches
|
||||
or persistence layers if possible.
|
||||
|
||||
### 2) Writing a fuzz test
|
||||
|
||||
First, you need to determine in your input data. The current test suite uses SSZ encoded bytes to
|
||||
deserialize to input objects.
|
||||
|
||||
_Example: Block header input data_
|
||||
|
||||
```go
|
||||
type InputBlockWithPrestate struct {
|
||||
StateID uint16
|
||||
Block *ethpb.BeaconBlock
|
||||
}
|
||||
```
|
||||
|
||||
You'll also want to add that struct to `//testing/fuzz:ssz_generated_files` to generate the custom fast SSZ
|
||||
methods for serialization to improve test performance.
|
||||
|
||||
Your fuzz test must accept a single argument of type `[]byte`. The return types are ignored by
|
||||
libfuzzer, but might be useful for other applications such as
|
||||
[beacon-fuzz](https://github.com/sigp/beacon-fuzz). Be sure to name your test file with the
|
||||
`_fuzz.go` suffix for consistency.
|
||||
|
||||
```go
|
||||
func MyExampleFuzz(b []byte) {
|
||||
input := &MyFuzzInputData{}
|
||||
if err := ssz.Unmarshal(b, input); err != nil {
|
||||
return // Input bytes doesn't serialize to input object.
|
||||
}
|
||||
|
||||
result, err := somePackage.MethodUnderTest(input)
|
||||
if err != nil {
|
||||
// Input was invalid for processing, but the method didn't panic so that's OK.
|
||||
return
|
||||
}
|
||||
// Optional: sanity check the resulting data.
|
||||
if result < 0 {
|
||||
panic("MethodUnderTest should never return a negative number") // Fail!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3) Add your fuzz target to fuzz/BUILD.bazel
|
||||
|
||||
Since we are using some custom rules to generate the fuzz test instrumentation and appropriate
|
||||
libfuzz testing suite, we cannot rely on gazelle to generate these targets for us.
|
||||
|
||||
```starlark
|
||||
go_fuzz_test(
|
||||
name = "example_fuzz_test",
|
||||
srcs = [
|
||||
"example_fuzz.go",
|
||||
] + COMMON_SRCS, # common and input type files.
|
||||
corpus = "example_corpus",
|
||||
corpus_path = "fuzz/example_corpus", # Path from root of project
|
||||
func = "MyExampleFuzz",
|
||||
importpath = IMPORT_PATH,
|
||||
deps = [
|
||||
# Deps used in your fuzz test.
|
||||
] + COMMON_DEPS,
|
||||
)
|
||||
```
|
||||
|
||||
Be sure to add your target to the test suite at `//testing/fuzz:fuzz_tests`.
|
||||
|
||||
### 4) Run your fuzz test
|
||||
|
||||
To run your fuzz test you must manually target it with bazel test and run with the config flag
|
||||
`--config=fuzz`.
|
||||
|
||||
```
|
||||
bazel test //testing/fuzz:example_fuzz_test --config=fuzz
|
||||
```
|
||||
|
||||
## Running fuzzit regression tests
|
||||
|
||||
To run fuzzit regression tests, you can run the fuzz test suite with the 1--config=fuzzit`
|
||||
configuration flag. Note: This requires docker installed on your machine. See
|
||||
[fuzzitdev/fuzzit#58](https://github.com/fuzzitdev/fuzzit/issues/58).
|
||||
|
||||
```
|
||||
bazel test //testing/fuzz:fuzz_tests --config=fuzzit
|
||||
```
|
||||
|
||||
If the same command above is run with the FUZZIT_API_KEY environment variable set, then the fuzzit
|
||||
test targets will be uploaded and restarted at https://app.fuzzit.dev.
|
||||
203
testing/fuzz/block_fuzz.go
Normal file
203
testing/fuzz/block_fuzz.go
Normal file
@@ -0,0 +1,203 @@
|
||||
// +build libfuzzer
|
||||
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
beaconkv "github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
p2pt "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
||||
powt "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
"github.com/prysmaticlabs/prysm/crypto/rand"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const topic = p2p.BlockSubnetTopicFormat
|
||||
|
||||
var db1 db.Database
|
||||
var dbPath = path.Join(os.TempDir(), "fuzz_beacondb", randomHex(6))
|
||||
|
||||
func randomHex(n int) string {
|
||||
bytes := make([]byte, n)
|
||||
if _, err := rand.NewGenerator().Read(bytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.PanicLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
|
||||
var err error
|
||||
|
||||
db1, err = db.NewDB(context.Background(), dbPath, &beaconkv.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupDB() {
|
||||
if err := db1.ClearDB(); err != nil {
|
||||
_ = err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
s, err := testutil.NewBeaconState()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b := testutil.NewBeaconBlock()
|
||||
if err := db1.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
br, err := b.HashTreeRoot()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := db1.SaveState(ctx, s, br); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := db1.SaveGenesisBlockRoot(ctx, br); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeChecker struct{}
|
||||
|
||||
func (fakeChecker) Syncing() bool {
|
||||
return false
|
||||
}
|
||||
func (fakeChecker) Initialized() bool {
|
||||
return false
|
||||
}
|
||||
func (fakeChecker) Status() error {
|
||||
return nil
|
||||
}
|
||||
func (fakeChecker) Resync() error {
|
||||
return nil
|
||||
}
|
||||
func (fakeChecker) Synced() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// FuzzBlock wraps BeaconFuzzBlock in a go-fuzz compatible interface
|
||||
func FuzzBlock(b []byte) int {
|
||||
BeaconFuzzBlock(b)
|
||||
return 0
|
||||
}
|
||||
|
||||
// BeaconFuzzBlock runs full processing of beacon block against a given state.
|
||||
func BeaconFuzzBlock(b []byte) {
|
||||
params.UseMainnetConfig()
|
||||
input := &InputBlockWithPrestate{}
|
||||
if err := input.UnmarshalSSZ(b); err != nil {
|
||||
return
|
||||
}
|
||||
st, err := v1.InitializeFromProtoUnsafe(input.State)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
setupDB()
|
||||
|
||||
p2p := p2pt.NewFuzzTestP2P()
|
||||
sgen := stategen.New(db1)
|
||||
sn := &testing.MockStateNotifier{}
|
||||
bn := &testing.MockBlockNotifier{}
|
||||
an := &testing.MockOperationNotifier{}
|
||||
ap := attestations.NewPool()
|
||||
ep := voluntaryexits.NewPool()
|
||||
sp := slashings.NewPool()
|
||||
ops, err := attestations.NewService(context.Background(), &attestations.Config{Pool: ap})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
chain, err := blockchain.NewService(context.Background(), &blockchain.Config{
|
||||
ChainStartFetcher: powt.NewPOWChain(),
|
||||
BeaconDB: db1,
|
||||
DepositCache: nil,
|
||||
AttPool: ap,
|
||||
ExitPool: ep,
|
||||
SlashingPool: sp,
|
||||
P2p: p2p,
|
||||
StateNotifier: sn,
|
||||
ForkChoiceStore: protoarray.New(0, 0, [32]byte{}),
|
||||
AttService: ops,
|
||||
StateGen: sgen,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
chain.Start()
|
||||
|
||||
s := sync.NewRegularSyncFuzz(&sync.Config{
|
||||
DB: db1,
|
||||
P2P: p2p,
|
||||
Chain: chain,
|
||||
InitialSync: fakeChecker{},
|
||||
StateNotifier: sn,
|
||||
BlockNotifier: bn,
|
||||
OperationNotifier: an,
|
||||
AttPool: ap,
|
||||
ExitPool: ep,
|
||||
SlashingPool: sp,
|
||||
StateGen: sgen,
|
||||
})
|
||||
|
||||
s.InitCaches()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p2p.Encoding().EncodeGossip(buf, input.Block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
pid := peer.ID("fuzz")
|
||||
msg := &pubsub.Message{
|
||||
Message: &pubsub_pb.Message{
|
||||
Data: buf.Bytes(),
|
||||
Topic: func() *string {
|
||||
tpc := topic
|
||||
return &tpc
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
if res := s.FuzzValidateBeaconBlockPubSub(ctx, pid, msg); res != pubsub.ValidationAccept {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.FuzzBeaconBlockSubscriber(ctx, input.Block); err != nil {
|
||||
_ = err
|
||||
}
|
||||
|
||||
if _, _, err := transition.ProcessBlockNoVerifyAnySig(ctx, st, wrapper.WrappedPhase0SignedBeaconBlock(input.Block)); err != nil {
|
||||
_ = err
|
||||
}
|
||||
}
|
||||
20
testing/fuzz/common.go
Normal file
20
testing/fuzz/common.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
)
|
||||
|
||||
// EnvBls defines an environment variable name to check whether BLS is enabled or not.
|
||||
const EnvBls = "BLS_ENABLED"
|
||||
|
||||
func init() {
|
||||
var blsEnabled bool
|
||||
if value, exists := os.LookupEnv(EnvBls); exists {
|
||||
blsEnabled = value == "1"
|
||||
}
|
||||
features.Init(&features.Flags{
|
||||
SkipBLSVerify: !blsEnabled,
|
||||
})
|
||||
}
|
||||
136
testing/fuzz/generated.ssz.go
Normal file
136
testing/fuzz/generated.ssz.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Code generated by fastssz. DO NOT EDIT.
|
||||
// Hash: d706f5f311b0f23294fc4b788d2f6a7ccd6fe644291047b6c6c08d115d07c680
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// MarshalSSZ ssz marshals the InputBlockWithPrestate object
|
||||
func (i *InputBlockWithPrestate) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(i)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the InputBlockWithPrestate object to a target array
|
||||
func (i *InputBlockWithPrestate) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(8)
|
||||
|
||||
// Offset (0) 'State'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
if i.State == nil {
|
||||
i.State = new(ethpb.BeaconState)
|
||||
}
|
||||
offset += i.State.SizeSSZ()
|
||||
|
||||
// Offset (1) 'Block'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
if i.Block == nil {
|
||||
i.Block = new(ethpb.SignedBeaconBlock)
|
||||
}
|
||||
offset += i.Block.SizeSSZ()
|
||||
|
||||
// Field (0) 'State'
|
||||
if dst, err = i.State.MarshalSSZTo(dst); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (1) 'Block'
|
||||
if dst, err = i.Block.MarshalSSZTo(dst); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the InputBlockWithPrestate object
|
||||
func (i *InputBlockWithPrestate) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 8 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o0, o1 uint64
|
||||
|
||||
// Offset (0) 'State'
|
||||
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o0 < 8 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Offset (1) 'Block'
|
||||
if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
// Field (0) 'State'
|
||||
{
|
||||
buf = tail[o0:o1]
|
||||
if i.State == nil {
|
||||
i.State = new(ethpb.BeaconState)
|
||||
}
|
||||
if err = i.State.UnmarshalSSZ(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Field (1) 'Block'
|
||||
{
|
||||
buf = tail[o1:]
|
||||
if i.Block == nil {
|
||||
i.Block = new(ethpb.SignedBeaconBlock)
|
||||
}
|
||||
if err = i.Block.UnmarshalSSZ(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the InputBlockWithPrestate object
|
||||
func (i *InputBlockWithPrestate) SizeSSZ() (size int) {
|
||||
size = 8
|
||||
|
||||
// Field (0) 'State'
|
||||
if i.State == nil {
|
||||
i.State = new(ethpb.BeaconState)
|
||||
}
|
||||
size += i.State.SizeSSZ()
|
||||
|
||||
// Field (1) 'Block'
|
||||
if i.Block == nil {
|
||||
i.Block = new(ethpb.SignedBeaconBlock)
|
||||
}
|
||||
size += i.Block.SizeSSZ()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the InputBlockWithPrestate object
|
||||
func (i *InputBlockWithPrestate) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(i)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the InputBlockWithPrestate object with a hasher
|
||||
func (i *InputBlockWithPrestate) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'State'
|
||||
if err = i.State.HashTreeRootWith(hh); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (1) 'Block'
|
||||
if err = i.Block.HashTreeRootWith(hh); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
11
testing/fuzz/inputs.go
Normal file
11
testing/fuzz/inputs.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// InputBlockWithPrestate for fuzz testing beacon blocks.
|
||||
type InputBlockWithPrestate struct {
|
||||
State *ethpb.BeaconState
|
||||
Block *ethpb.SignedBeaconBlock
|
||||
}
|
||||
0
testing/fuzz/rpc_status_corpus/.gitkeep
Normal file
0
testing/fuzz/rpc_status_corpus/.gitkeep
Normal file
89
testing/fuzz/rpc_status_fuzz.go
Normal file
89
testing/fuzz/rpc_status_fuzz.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/libp2p/go-libp2p"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/pkg/errors"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
regularsync "github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var p *p2p.Service
|
||||
var h host.Host
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.PanicLevel)
|
||||
|
||||
var err error
|
||||
p, err = p2p.NewService(context.Background(), &p2p.Config{
|
||||
NoDiscovery: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not create new p2p service"))
|
||||
}
|
||||
|
||||
h, err = libp2p.New(context.Background())
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not create new libp2p host"))
|
||||
}
|
||||
|
||||
info := peer.AddrInfo{
|
||||
ID: h.ID(),
|
||||
Addrs: h.Addrs(),
|
||||
}
|
||||
if err := p.Connect(info); err != nil {
|
||||
panic(errors.Wrap(err, "could not connect to peer"))
|
||||
}
|
||||
regularsync.NewService(context.Background(), ®ularsync.Config{
|
||||
P2P: p,
|
||||
DB: nil,
|
||||
AttPool: nil,
|
||||
ExitPool: nil,
|
||||
SlashingPool: nil,
|
||||
Chain: &mock.ChainService{
|
||||
Root: bytesutil.PadTo([]byte("root"), 32),
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)},
|
||||
Fork: ðpb.Fork{CurrentVersion: []byte("foo")},
|
||||
},
|
||||
StateNotifier: (&mock.ChainService{}).StateNotifier(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
InitialSync: &mockSync.Sync{IsSyncing: false},
|
||||
BlockNotifier: nil,
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzP2PRPCStatus wraps BeaconFuzzP2PRPCStatus in a go-fuzz compatible interface
|
||||
func FuzzP2PRPCStatus(b []byte) int {
|
||||
BeaconFuzzP2PRPCStatus(b)
|
||||
return 0
|
||||
}
|
||||
|
||||
// BeaconFuzzP2PRPCStatus implements libfuzzer and beacon fuzz interface.
|
||||
func BeaconFuzzP2PRPCStatus(b []byte) {
|
||||
s, err := h.NewStream(context.Background(), p.PeerID(), "/eth2/beacon_chain/req/status/1/ssz_snappy")
|
||||
if err != nil {
|
||||
// libp2p ¯\_(ツ)_/¯
|
||||
if strings.Contains(err.Error(), "stream reset") || strings.Contains(err.Error(), "connection reset by peer") || strings.Contains(err.Error(), "max dial attempts exceeded") {
|
||||
return
|
||||
}
|
||||
panic(errors.Wrap(err, "failed to open stream"))
|
||||
}
|
||||
if s == nil {
|
||||
panic("nil stream")
|
||||
}
|
||||
defer func() {
|
||||
err := s.Close()
|
||||
_ = err
|
||||
}()
|
||||
_, err = s.Write(b)
|
||||
_ = err
|
||||
}
|
||||
27
testing/fuzz/ssz_encoder_attestations_fuzz.go
Normal file
27
testing/fuzz/ssz_encoder_attestations_fuzz.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var buf = new(bytes.Buffer)
|
||||
|
||||
// SszEncoderAttestationFuzz runs network encode/decode for attestations.
|
||||
func SszEncoderAttestationFuzz(b []byte) {
|
||||
params.UseMainnetConfig()
|
||||
buf.Reset()
|
||||
input := ðpb.Attestation{}
|
||||
e := encoder.SszNetworkEncoder{}
|
||||
if err := e.DecodeGossip(b, input); err != nil {
|
||||
_ = err
|
||||
return
|
||||
}
|
||||
if _, err := e.EncodeGossip(buf, input); err != nil {
|
||||
_ = err
|
||||
return
|
||||
}
|
||||
}
|
||||
65
testing/fuzz/state_fuzz.go
Normal file
65
testing/fuzz/state_fuzz.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
stateutil "github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func init() {
|
||||
features.Init(&features.Flags{
|
||||
EnableSSZCache: false,
|
||||
})
|
||||
}
|
||||
|
||||
// BeaconStateFuzz --
|
||||
func BeaconStateFuzz(input []byte) {
|
||||
params.UseMainnetConfig()
|
||||
st := ðpb.BeaconState{}
|
||||
if err := st.UnmarshalSSZ(input); err != nil {
|
||||
return
|
||||
}
|
||||
s, err := v1.InitializeFromProtoUnsafe(st)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
validateStateHTR(s)
|
||||
nextEpoch := core.SlotToEpoch(s.Slot()) + 1
|
||||
slot, err := core.StartSlot(nextEpoch)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := stateutil.ProcessSlots(context.Background(), s, slot); err != nil {
|
||||
_ = err
|
||||
return
|
||||
}
|
||||
validateStateHTR(s)
|
||||
}
|
||||
|
||||
func validateStateHTR(s *v1.BeaconState) {
|
||||
rawState, ok := s.InnerStateUnsafe().(*ethpb.BeaconState)
|
||||
if !ok {
|
||||
panic("non valid type assertion")
|
||||
}
|
||||
rt, err := s.HashTreeRoot(context.Background())
|
||||
nxtRt, err2 := rawState.HashTreeRoot()
|
||||
|
||||
if err == nil && err2 != nil {
|
||||
panic("HTR from state had only and error from cached state HTR method")
|
||||
}
|
||||
if err != nil && err2 == nil {
|
||||
panic("HTR from state had only and error from fast-ssz HTR method")
|
||||
}
|
||||
if err != nil && err2 != nil {
|
||||
return
|
||||
}
|
||||
if rt != nxtRt {
|
||||
panic(fmt.Sprintf("cached HTR gave a root of %#x while fast-ssz gave a root of %#x", rt, nxtRt))
|
||||
}
|
||||
}
|
||||
25
testing/fuzz/testing/BUILD.bazel
Normal file
25
testing/fuzz/testing/BUILD.bazel
Normal file
@@ -0,0 +1,25 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
testonly = 1,
|
||||
srcs = ["beacon_fuzz_states.go"],
|
||||
data = [
|
||||
"@sigp_beacon_fuzz_corpora//:current_mainnet_beaconstate",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/testing/fuzz/testing",
|
||||
visibility = [
|
||||
"//testing/fuzz:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["beacon_fuzz_states_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//shared/testutil/require:go_default_library"],
|
||||
)
|
||||
39
testing/fuzz/testing/beacon_fuzz_states.go
Normal file
39
testing/fuzz/testing/beacon_fuzz_states.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
const fileBase = "0-11-0/mainnet/beaconstate"
|
||||
const fileBaseENV = "BEACONSTATES_PATH"
|
||||
|
||||
// BeaconFuzzState returns a beacon state by ID using the beacon-fuzz corpora.
|
||||
func BeaconFuzzState(ID uint64) (*ethpb.BeaconState, error) {
|
||||
base := fileBase
|
||||
// Using an environment variable allows a host image to specify the path when only the binary
|
||||
// executable was uploaded (without the runfiles). i.e. fuzzit's platform.
|
||||
if p, ok := os.LookupEnv(fileBaseENV); ok {
|
||||
base = p
|
||||
}
|
||||
ok, err := testutil.BazelDirectoryNonEmpty(base)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Beacon states directory (%s) does not exist or has no files.", base))
|
||||
}
|
||||
b, err := testutil.BazelFileBytes(base, strconv.Itoa(int(ID)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := ðpb.BeaconState{}
|
||||
if err := st.UnmarshalSSZ(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
12
testing/fuzz/testing/beacon_fuzz_states_test.go
Normal file
12
testing/fuzz/testing/beacon_fuzz_states_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestGetBeaconFuzzState(t *testing.T) {
|
||||
_, err := BeaconFuzzState(1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user