From d89658b8d5428a7f72baba496704ec8b85c9e191 Mon Sep 17 00:00:00 2001 From: anupsv Date: Fri, 20 Dec 2024 03:20:06 +0530 Subject: [PATCH] Add fuzz tests (#137) --- .github/workflows/fuzz-tests.yml | 30 +++++++++ Makefile | 5 ++ e2e/main_test.go | 4 +- e2e/server_fuzz_test.go | 46 ++++++++++++++ e2e/server_test.go | 105 ++++++++++++++++++++++++++++++- 5 files changed, 186 insertions(+), 4 deletions(-) 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 00000000..8608e3c9 --- /dev/null +++ b/.github/workflows/fuzz-tests.yml @@ -0,0 +1,30 @@ +name: fuzz-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 80eb09da..33f5d364 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ LDFLAGSSTRING +=-X main.Date=$(BUILD_TIME) LDFLAGSSTRING +=-X main.Version=$(GIT_TAG) LDFLAGS := -ldflags "$(LDFLAGSSTRING)" +E2EFUZZTEST = FUZZ=true go test ./e2e -fuzz -v -fuzztime=15m + .PHONY: eigenda-proxy eigenda-proxy: env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/eigenda-proxy ./cmd/server @@ -35,6 +37,9 @@ test: e2e-test: INTEGRATION=true go test -timeout 1m ./e2e -parallel 4 +e2e-fuzz-test: + $(E2EFUZZTEST) + holesky-test: TESTNET=true go test -timeout 50m ./e2e -parallel 4 diff --git a/e2e/main_test.go b/e2e/main_test.go index 0af174fb..c0b397e8 100644 --- a/e2e/main_test.go +++ b/e2e/main_test.go @@ -24,13 +24,13 @@ import ( var ( runTestnetIntegrationTests bool // holesky tests runIntegrationTests bool // memstore tests + runFuzzTests bool // fuzz tests ) // ParseEnv ... reads testing cfg fields. Go test flags don't work for this library due to the dependency on Optimism's E2E framework // which initializes test flags per init function which is called before an init in this package. func ParseEnv() { - runIntegrationTests = os.Getenv("INTEGRATION") == "true" || os.Getenv("INTEGRATION") == "1" - runTestnetIntegrationTests = os.Getenv("TESTNET") == "true" || os.Getenv("TESTNET") == "1" + runFuzzTests = os.Getenv("FUZZ") == "true" || os.Getenv("FUZZ") == "1" if runIntegrationTests && runTestnetIntegrationTests { panic("only one of INTEGRATION=true or TESTNET=true env var can be set") } diff --git a/e2e/server_fuzz_test.go b/e2e/server_fuzz_test.go new file mode 100644 index 00000000..6eee2128 --- /dev/null +++ b/e2e/server_fuzz_test.go @@ -0,0 +1,46 @@ +package e2e_test + +import ( + "github.com/stretchr/testify/assert" + + "testing" + "unicode" + + "github.com/Layr-Labs/eigenda-proxy/client" + "github.com/Layr-Labs/eigenda-proxy/e2e" +) + +// FuzzProxyClientServerIntegrationAndOpClientKeccak256MalformedInputs will fuzz the proxy client server integration +// and op client keccak256 with malformed inputs. This is never meant to be fuzzed with EigenDA. +func FuzzProxyClientServerIntegration(f *testing.F) { + if !runFuzzTests { + f.Skip("Skipping test as FUZZ env var not set") + } + + tsConfig := e2e.TestSuiteConfig(e2e.TestConfig(useMemory())) + ts, kill := e2e.CreateTestSuite(tsConfig) + + for r := rune(0); r <= unicode.MaxRune; r++ { + if unicode.IsPrint(r) { + f.Add([]byte(string(r))) // Add each printable Unicode character as a seed + } + } + + cfg := &client.Config{ + URL: ts.Address(), + } + + daClient := client.New(cfg) + + // seed and data are expected. `seed` value is seed: {rune} and data is the one with the random byte(s) + f.Fuzz(func(t *testing.T, data []byte) { + _, err := daClient.SetData(ts.Ctx, data) + assert.NoError(t, err) + if err != nil { + t.Errorf("Failed to set data: %v", err) + } + }) + + f.Cleanup(kill) + +} diff --git a/e2e/server_test.go b/e2e/server_test.go index 380da2bf..10ccf135 100644 --- a/e2e/server_test.go +++ b/e2e/server_test.go @@ -1,15 +1,16 @@ package e2e_test import ( + "strings" "testing" "time" "github.com/Layr-Labs/eigenda-proxy/client" "github.com/Layr-Labs/eigenda-proxy/commitments" "github.com/Layr-Labs/eigenda-proxy/common" - "github.com/stretchr/testify/require" - "github.com/Layr-Labs/eigenda-proxy/e2e" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func useMemory() bool { @@ -29,6 +30,7 @@ func TestOptimismClientWithKeccak256Commitment(t *testing.T) { tsConfig := e2e.TestSuiteConfig(testCfg) ts, kill := e2e.CreateTestSuite(tsConfig) defer kill() + requireOPClientSetGet(t, ts, e2e.RandBytes(100), true) } @@ -52,6 +54,105 @@ func TestOptimismClientWithGenericCommitment(t *testing.T) { requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.OptimismGeneric) } +// TestProxyClientServerIntegration tests the proxy client and server integration by setting the data as a single byte, +// many unicode characters, single unicode character and an empty preimage. It then tries to get the data from the +// proxy server with empty byte, single byte and random string. +func TestProxyClientServerIntegration(t *testing.T) { + t.Parallel() + + if !runIntegrationTests && !runTestnetIntegrationTests { + t.Skip("Skipping test as INTEGRATION or TESTNET env var not set") + } + + tsConfig := e2e.TestSuiteConfig(e2e.TestConfig(useMemory())) + ts, kill := e2e.CreateTestSuite(tsConfig) + t.Cleanup(kill) + + cfg := &client.Config{ + URL: ts.Address(), + } + daClient := client.New(cfg) + + t.Run("single byte preimage set data case", func(t *testing.T) { + t.Parallel() + testPreimage := []byte{1} // single byte preimage + t.Log("Setting input data on proxy server...") + _, err := daClient.SetData(ts.Ctx, testPreimage) + require.NoError(t, err) + }) + + t.Run("unicode preimage set data case", func(t *testing.T) { + t.Parallel() + testPreimage := []byte("§§©ˆªªˆ˙√ç®∂§∞¶§ƒ¥√¨¥√¨¥ƒƒ©˙˜ø˜˜˜∫˙∫¥∫√†®®√稈¨˙ï") // many unicode characters + t.Log("Setting input data on proxy server...") + _, err := daClient.SetData(ts.Ctx, testPreimage) + require.NoError(t, err) + + testPreimage = []byte("§") // single unicode character + t.Log("Setting input data on proxy server...") + _, err = daClient.SetData(ts.Ctx, testPreimage) + require.NoError(t, err) + + }) + + t.Run("empty preimage set data case", func(t *testing.T) { + t.Parallel() + testPreimage := []byte("") // Empty preimage + t.Log("Setting input data on proxy server...") + _, err := daClient.SetData(ts.Ctx, testPreimage) + require.NoError(t, err) + }) + + t.Run("get data edge cases", func(t *testing.T) { + t.Parallel() + testCert := []byte("") + _, err := daClient.GetData(ts.Ctx, testCert) + require.Error(t, err) + assert.True(t, strings.Contains(err.Error(), + "404") && !isNilPtrDerefPanic(err.Error())) + + testCert = []byte{1} + _, err = daClient.GetData(ts.Ctx, testCert) + require.Error(t, err) + assert.True(t, strings.Contains(err.Error(), + "400") && !isNilPtrDerefPanic(err.Error())) + + testCert = e2e.RandBytes(10000) + _, err = daClient.GetData(ts.Ctx, testCert) + require.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "400") && !isNilPtrDerefPanic(err.Error())) + }) + +} + +func TestProxyClient(t *testing.T) { + if !runIntegrationTests && !runTestnetIntegrationTests { + t.Skip("Skipping test as INTEGRATION or TESTNET env var not set") + } + + t.Parallel() + + tsConfig := e2e.TestSuiteConfig(e2e.TestConfig(useMemory())) + ts, kill := e2e.CreateTestSuite(tsConfig) + defer kill() + + cfg := &client.Config{ + URL: ts.Address(), + } + daClient := client.New(cfg) + + testPreimage := e2e.RandBytes(100) + + t.Log("Setting input data on proxy server...") + blobInfo, err := daClient.SetData(ts.Ctx, testPreimage) + require.NoError(t, err) + + t.Log("Getting input data from proxy server...") + preimage, err := daClient.GetData(ts.Ctx, blobInfo) + require.NoError(t, err) + require.Equal(t, testPreimage, preimage) +} + func TestProxyClientWriteRead(t *testing.T) { if !runIntegrationTests && !runTestnetIntegrationTests { t.Skip("Skipping test as INTEGRATION or TESTNET env var not set")