diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0d7e8b79..14de57ab 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,8 +22,10 @@ jobs: working-directory: tests/go-tests - name: Run tests run: go test -v ./rvgo/... -coverprofile=coverage.out -coverpkg=./rvgo/... - - name: Fuzz + - name: Fuzz syscall run: make fuzz + - name: Fuzz parsing + run: make fuzz-parsing - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: diff --git a/Makefile b/Makefile index 81e739c8..1578415b 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,21 @@ fuzz-mac: fuzz \ fuzz-mac +quick = '10s' +hour = '1h' +daily = '24h' +weekend = '2 days' +fuzztime = $(quick) +fuzz-parsing: build + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeI ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeS ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeB ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeU ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeJ ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseOpcode ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseRd ./rvgo/test +.PHONY: fuzz-parsing + OP_PROGRAM_PATH ?= $(MONOREPO_ROOT)/op-program/bin-riscv/op-program-client-riscv.elf prestate: build-rvgo op-program-riscv diff --git a/rvgo/diff/DifferentialParsingFuzzTests.md b/rvgo/diff/DifferentialParsingFuzzTests.md new file mode 100644 index 00000000..4c757ecd --- /dev/null +++ b/rvgo/diff/DifferentialParsingFuzzTests.md @@ -0,0 +1,22 @@ +# Running + +```bash +make fuzz-parsing +``` + +or + +```bash +fuzz-parsing: build + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeI ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeS ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeB ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeU ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseTypeJ ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseOpcode ./rvgo/test + go test -run NOTAREALTEST -v -fuzztime $(fuzztime) -fuzz=FuzzParseRd ./rvgo/test +``` + +## How it works + +This test is relatively simple in comparison to others – it implements each Parse function, and calls slow and fast to ensure that the output from both are identical. This pattern is meant to show the recommended way to test these functions when they are publicly accessible, such that you can compare to ensure that fast and slow behave exactly as expected. The EVM implementation of these differential fuzzing tests do not use go fuzz, and use foundry ffi to call out to a `slow` executable instead. \ No newline at end of file diff --git a/rvgo/fast/fast-parse.go b/rvgo/fast/fast-parse.go new file mode 100644 index 00000000..cf9eaf0b --- /dev/null +++ b/rvgo/fast/fast-parse.go @@ -0,0 +1,44 @@ +package fast + +func ParseImmTypeI(instr U64) U64 { + return parseImmTypeI(instr) +} + +func ParseImmTypeS(instr U64) U64 { + return parseImmTypeS(instr) +} + +func ParseImmTypeB(instr U64) U64 { + return parseImmTypeB(instr) +} + +func ParseImmTypeU(instr U64) U64 { + return parseImmTypeU(instr) +} + +func ParseImmTypeJ(instr U64) U64 { + return parseImmTypeJ(instr) +} + +func ParseOpcode(instr U64) U64 { + return parseOpcode(instr) +} + +func ParseRd(instr U64) U64 { + return parseRd(instr) +} + +func ParseFunct3(instr U64) U64 { + return parseFunct3(instr) +} + +func ParseRs1(instr U64) U64 { + return parseRs1(instr) +} + +func ParseRs2(instr U64) U64 { + return parseRs2(instr) +} +func ParseFunct7(instr U64) U64 { + return parseFunct7(instr) +} diff --git a/rvgo/slow/slow-parse.go b/rvgo/slow/slow-parse.go new file mode 100644 index 00000000..24c5af1a --- /dev/null +++ b/rvgo/slow/slow-parse.go @@ -0,0 +1,49 @@ +package slow + +func ParseImmTypeI(instr U64) U64 { + return parseImmTypeI(instr) +} + +func ParseImmTypeS(instr U64) U64 { + return parseImmTypeS(instr) +} + +func ParseImmTypeB(instr U64) U64 { + return parseImmTypeB(instr) +} + +func ParseImmTypeU(instr U64) U64 { + return parseImmTypeU(instr) +} + +func ParseImmTypeJ(instr U64) U64 { + return parseImmTypeJ(instr) +} + +func ParseOpcode(instr U64) U64 { + return parseOpcode(instr) +} + +func ParseRd(instr U64) U64 { + return parseRd(instr) +} + +func ParseFunct3(instr U64) U64 { + return parseFunct3(instr) +} + +func ParseRs1(instr U64) U64 { + return parseRs1(instr) +} + +func ParseRs2(instr U64) U64 { + return parseRs2(instr) +} + +func ParseFunct7(instr U64) U64 { + return parseFunct7(instr) +} + +func Val(v U64) uint64 { + return v.val() +} diff --git a/rvgo/test/diff_test.go b/rvgo/test/diff_test.go new file mode 100644 index 00000000..de6a705d --- /dev/null +++ b/rvgo/test/diff_test.go @@ -0,0 +1,67 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/asterisc/rvgo/fast" + "github.com/ethereum-optimism/asterisc/rvgo/slow" +) + +func FuzzParseTypeI(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseImmTypeI(slow.U64{instr}) + var fastOutput = fast.ParseImmTypeI(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseTypeS(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseImmTypeS(slow.U64{instr}) + var fastOutput = fast.ParseImmTypeS(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseTypeB(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseImmTypeB(slow.U64{instr}) + var fastOutput = fast.ParseImmTypeB(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseTypeU(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseImmTypeU(slow.U64{instr}) + var fastOutput = fast.ParseImmTypeU(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseTypeJ(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseImmTypeJ(slow.U64{instr}) + var fastOutput = fast.ParseImmTypeJ(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseOpcode(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseOpcode(slow.U64{instr}) + var fastOutput = fast.ParseOpcode(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +} +func FuzzParseRd(f *testing.F) { + f.Fuzz(func(t *testing.T, instr uint64) { + var slowOutput = slow.ParseRd(slow.U64{instr}) + var fastOutput = fast.ParseRd(fast.U64(instr)) + + require.Equal(t, slow.Val(slowOutput), fastOutput) + }) +}