From e331cbdbe6c51b8a2cf78d18477b026fa27558a2 Mon Sep 17 00:00:00 2001 From: anupsv Date: Thu, 19 Sep 2024 21:28:13 -0700 Subject: [PATCH] adding fuzzer cases --- .github/workflows/fuzz-tests.yml | 30 +++++++++ Makefile | 6 ++ e2e/server_fuzz_test.go | 66 ++++++++++++++++++ e2e/setup.go | 112 +++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+) create mode 100644 .github/workflows/fuzz-tests.yml create mode 100644 e2e/server_fuzz_test.go diff --git a/.github/workflows/fuzz-tests.yml b/.github/workflows/fuzz-tests.yml new file mode 100644 index 0000000..5f86d27 --- /dev/null +++ b/.github/workflows/fuzz-tests.yml @@ -0,0 +1,30 @@ +name: unit-tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + go-test: + outputs: + COVERAGE: ${{ steps.unit.outputs.coverage }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.21 + + - name: Install project dependencies + run: | + go mod download + + - name: Run E2E Fuzz Tests + run: | + make e2e-fuzz-test diff --git a/Makefile b/Makefile index ca54a85..1ddb400 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ LDFLAGSSTRING +=-X main.Version=$(GIT_TAG) LDFLAGS := -ldflags "$(LDFLAGSSTRING)" E2ETEST = INTEGRATION=true go test -timeout 1m -v ./e2e -parallel 4 -deploy-config ../.devnet/devnetL1.json +E2EFUZZTEST = INTEGRATION=true go test -fuzz ./e2e -deploy-config ../.devnet/devnetL1.json -v -fuzztime=30m HOLESKYTEST = TESTNET=true go test -timeout 50m -v ./e2e -parallel 4 -deploy-config ../.devnet/devnetL1.json .PHONY: eigenda-proxy @@ -56,6 +57,11 @@ e2e-test: stop-minio stop-redis run-minio run-redis make stop-minio && \ make stop-redis +e2e-fuzz-test: stop-minio stop-redis run-minio run-redis + $(E2ETEST) && \ + make stop-minio && \ + make stop-redis + holesky-test: stop-minio stop-redis run-minio run-redis $(HOLESKYTEST) && \ make stop-minio && \ diff --git a/e2e/server_fuzz_test.go b/e2e/server_fuzz_test.go new file mode 100644 index 0000000..27e03ae --- /dev/null +++ b/e2e/server_fuzz_test.go @@ -0,0 +1,66 @@ +package e2e_test + +import ( + "github.com/Layr-Labs/eigenda-proxy/client" + "github.com/Layr-Labs/eigenda-proxy/e2e" + op_plasma "github.com/ethereum-optimism/optimism/op-plasma" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func FuzzProxyClientServerIntegration(f *testing.F) { + //if !runIntegrationTests && !runTestnetIntegrationTests { + // f.Skip("Skipping test as INTEGRATION or TESTNET env var not set") + //} + + testCfg := e2e.TestConfig(useMemory()) + testCfg.UseKeccak256ModeS3 = true + tsConfig := e2e.TestSuiteConfigF(f, testCfg) + ts, kill := e2e.CreateTestSuiteF(f, tsConfig) + defer kill() + + cfg := &client.Config{ + URL: ts.Address(), + } + daClient := client.New(cfg) + + // Add each printable Unicode character as a seed including ascii + //for r := rune(0); r <= unicode.MaxRune; r++ { + // if unicode.IsPrint(r) { + // f.Add(fmt.Sprintf("seed: %s", string(r)), []byte(string(r))) // Add each printable Unicode character as a seed + // } + //} + + f.Fuzz(func(t *testing.T, seed string, data []byte) { + _, err := daClient.SetData(ts.Ctx, data) + require.NoError(t, err) + }) +} + +func FuzzOpClientKeccak256MalformedInputs(f *testing.F) { + if !runIntegrationTests || runTestnetIntegrationTests { + f.Skip("Skipping test as INTEGRATION var not set") + } + + testCfg := e2e.TestConfig(useMemory()) + testCfg.UseKeccak256ModeS3 = true + tsConfig := e2e.TestSuiteConfigF(f, testCfg) + ts, kill := e2e.CreateTestSuiteF(f, tsConfig) + defer kill() + + daClientPcFalse := op_plasma.NewDAClient(ts.Address(), false, false) + + // Fuzz the SetInput function with random data + // seed and data are expected. `seed` value is seed: {i} and data is the one with the random string + f.Fuzz(func(t *testing.T, seed string, data []byte) { + + _, err := daClientPcFalse.SetInput(ts.Ctx, data) + // should fail with proper error message as is now, and cannot contain panics or nils + if err != nil { + assert.True(t, !isNilPtrDerefPanic(err.Error())) + } + + }) + +} diff --git a/e2e/setup.go b/e2e/setup.go index b91cdd6..de7f9b6 100644 --- a/e2e/setup.go +++ b/e2e/setup.go @@ -6,6 +6,7 @@ import ( "os" "testing" "time" + "unicode" "github.com/Layr-Labs/eigenda-proxy/metrics" "github.com/Layr-Labs/eigenda-proxy/server" @@ -156,6 +157,77 @@ func TestSuiteConfig(t *testing.T, testCfg *Cfg) server.CLIConfig { return cfg } +func TestSuiteConfigF(t *testing.F, testCfg *Cfg) server.CLIConfig { + // load signer key from environment + pk := os.Getenv(privateKey) + if pk == "" && !testCfg.UseMemory { + t.Fatal("SIGNER_PRIVATE_KEY environment variable not set") + } + + // load node url from environment + ethRPC := os.Getenv(ethRPC) + if ethRPC == "" && !testCfg.UseMemory { + t.Fatal("ETHEREUM_RPC environment variable is not set") + } + + var pollInterval time.Duration + if testCfg.UseMemory { + pollInterval = time.Second * 1 + } else { + pollInterval = time.Minute * 1 + } + + eigendaCfg := server.Config{ + ClientConfig: clients.EigenDAClientConfig{ + RPC: holeskyDA, + StatusQueryTimeout: time.Minute * 45, + StatusQueryRetryInterval: pollInterval, + DisableTLS: false, + SignerPrivateKeyHex: pk, + }, + EthRPC: ethRPC, + SvcManagerAddr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b", // incompatible with non holeskly networks + CacheDir: "../resources/SRSTables", + G1Path: "../resources/g1.point", + MaxBlobLength: "16mib", + G2PowerOfTauPath: "../resources/g2.point.powerOf2", + PutBlobEncodingVersion: 0x00, + MemstoreEnabled: testCfg.UseMemory, + MemstoreBlobExpiration: testCfg.Expiration, + EthConfirmationDepth: 0, + } + + if testCfg.UseMemory { + eigendaCfg.ClientConfig.SignerPrivateKeyHex = "0000000000000000000100000000000000000000000000000000000000000000" + } + + var cfg server.CLIConfig + switch { + case testCfg.UseKeccak256ModeS3: + cfg = createS3Config(eigendaCfg) + + case testCfg.UseS3Caching: + eigendaCfg.CacheTargets = []string{"S3"} + cfg = createS3Config(eigendaCfg) + + case testCfg.UseS3Fallback: + eigendaCfg.FallbackTargets = []string{"S3"} + cfg = createS3Config(eigendaCfg) + + case testCfg.UseRedisCaching: + eigendaCfg.CacheTargets = []string{"redis"} + cfg = createRedisConfig(eigendaCfg) + + default: + cfg = server.CLIConfig{ + EigenDAConfig: eigendaCfg, + MetricsCfg: opmetrics.CLIConfig{}, + } + } + + return cfg +} + type TestSuite struct { Ctx context.Context Log log.Logger @@ -195,6 +267,46 @@ func CreateTestSuite(t *testing.T, testSuiteCfg server.CLIConfig) (TestSuite, fu }, kill } +func CreateTestSuiteF(t *testing.F, testSuiteCfg server.CLIConfig) (TestSuite, func()) { + + for r := rune(0); r <= unicode.MaxRune; r++ { + if unicode.IsPrint(r) { + t.Add(fmt.Sprintf("seed: %s", string(r)), []byte(string(r))) // Add each printable Unicode character as a seed + } + } + + log := oplog.NewLogger(os.Stdout, oplog.CLIConfig{ + Level: log.LevelDebug, + Format: oplog.FormatLogFmt, + Color: true, + }).New("role", svcName) + + ctx := context.Background() + store, err := server.LoadStoreRouter( + ctx, + testSuiteCfg, + log, + ) + require.NoError(t, err) + server := server.NewServer(host, 0, store, log, metrics.NoopMetrics) + + t.Log("Starting proxy server...") + err = server.Start() + require.NoError(t, err) + + kill := func() { + if err := server.Stop(); err != nil { + panic(err) + } + } + + return TestSuite{ + Ctx: ctx, + Log: log, + Server: server, + }, kill +} + func (ts *TestSuite) Address() string { // read port from listener port := ts.Server.Port()