Commit fde52b49 authored by mbaxter's avatar mbaxter Committed by GitHub

cannon: Enable 64-bit tests (#12729)

* cannon: Set up fuzz tests to run across 32- and 64-bit VMs

Update preimage read fuzz test to work on 64-bit vms

* cannon: Run both 32- and 64-bit Cannon tests in CI

* cannon: Separate log and coverage files for 32- and 64-bit tests

* cannon: Clean up evm validation code

* cannon: Move skip utility to testutil

* cannon: Make TestState_EncodeWitness 64-bit compatible

* cannon: Skip failing 64-bit tests

* cannon: Update TestEVM_EmptyThreadStacks to work across 32- and 64-bit vms

* cannon: Update version state tests, add build tags

* cannon: Fix code coverage upload paths

* cannon: Fix code coverage file extensions

* cannon: Run 32/64-bit tests using a matrix

* cannon: Try to set better name for arch-specific cannon tests

* cannon: Use generated job names in bedrock requirements list

* cannon: Add comment on new validator util

---------
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>
parent 1b4fda30
...@@ -162,9 +162,9 @@ jobs: ...@@ -162,9 +162,9 @@ jobs:
description: Whether to notify on failure description: Whether to notify on failure
type: boolean type: boolean
default: false default: false
mips64: mips_word_size:
type: boolean type: integer
default: false default: 32
steps: steps:
- checkout - checkout
- check-changed: - check-changed:
...@@ -187,28 +187,34 @@ jobs: ...@@ -187,28 +187,34 @@ jobs:
working_directory: cannon working_directory: cannon
- when: - when:
condition: condition:
not: <<parameters.mips64>> equal: [32, <<parameters.mips_word_size>>]
steps: steps:
- run: - run:
name: Cannon Go 32-bit tests name: Cannon Go 32-bit tests
command: | command: |
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>> export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
gotestsum --format=testname --junitfile=../tmp/test-results/cannon.xml --jsonfile=../tmp/testlogs/log.json \ gotestsum --format=testname --junitfile=../tmp/test-results/cannon-32.xml --jsonfile=../tmp/testlogs/log-32.json \
-- -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... -- -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-32.out ./...
working_directory: cannon
- run:
name: Upload Cannon coverage
command: codecov --verbose --clean --flags cannon-go-tests-32 -f ./coverage-32.out
working_directory: cannon working_directory: cannon
- when: - when:
condition: <<parameters.mips64>> condition:
equal: [64, <<parameters.mips_word_size>>]
steps: steps:
- run: - run:
name: Cannon Go 64-bit tests name: Cannon Go 64-bit tests
command: | command: |
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>> export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
gotestsum --format=testname --junitfile=../tmp/test-results/cannon.xml --jsonfile=../tmp/testlogs/log.json \ gotestsum --format=testname --junitfile=../tmp/test-results/cannon-64.xml --jsonfile=../tmp/testlogs/log-64.json \
-- --tags=cannon64 -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... -- --tags=cannon64 -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-64.out ./...
working_directory: cannon
- run:
name: Upload Cannon coverage
command: codecov --verbose --clean --flags cannon-go-tests-64 -f ./coverage-64.out
working_directory: cannon working_directory: cannon
- run:
name: upload Cannon coverage
command: codecov --verbose --clean --flags cannon-go-tests
- store_test_results: - store_test_results:
path: ./tmp/test-results path: ./tmp/test-results
- store_artifacts: - store_artifacts:
...@@ -1365,7 +1371,8 @@ workflows: ...@@ -1365,7 +1371,8 @@ workflows:
- go-mod-download - go-mod-download
- go-lint - go-lint
- cannon-build-test-vectors - cannon-build-test-vectors
- cannon-go-lint-and-test - cannon-go-lint-and-test-32-bit
- cannon-go-lint-and-test-64-bit
- check-generated-mocks-op-node - check-generated-mocks-op-node
- check-generated-mocks-op-service - check-generated-mocks-op-service
- go-mod-download - go-mod-download
...@@ -1403,10 +1410,14 @@ workflows: ...@@ -1403,10 +1410,14 @@ workflows:
- check-generated-mocks-op-node - check-generated-mocks-op-node
- check-generated-mocks-op-service - check-generated-mocks-op-service
- cannon-go-lint-and-test: - cannon-go-lint-and-test:
name: cannon-go-lint-and-test-<<matrix.mips_word_size>>-bit
requires: requires:
- contracts-bedrock-build - contracts-bedrock-build
skip_slow_tests: true skip_slow_tests: true
notify: true notify: true
matrix:
parameters:
mips_word_size: [ 32, 64 ]
- cannon-build-test-vectors - cannon-build-test-vectors
- todo-issues: - todo-issues:
name: todo-issues-check name: todo-issues-check
...@@ -1605,10 +1616,14 @@ workflows: ...@@ -1605,10 +1616,14 @@ workflows:
- contracts-bedrock-build: - contracts-bedrock-build:
skip_pattern: test skip_pattern: test
- cannon-go-lint-and-test: - cannon-go-lint-and-test:
name: cannon-go-lint-and-test-<<matrix.mips_word_size>>-bit
requires: requires:
- contracts-bedrock-build - contracts-bedrock-build
context: context:
- slack - slack
matrix:
parameters:
mips_word_size: [ 32, 64 ]
scheduled-docker-publish: scheduled-docker-publish:
when: when:
......
...@@ -87,19 +87,28 @@ cannon-stf-verify: ...@@ -87,19 +87,28 @@ cannon-stf-verify:
fuzz: fuzz:
printf "%s\n" \ printf "%s\n" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallBrk64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallMmap64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallExitGroup64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallFcntl64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintRead64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintWrite64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallCloneMT64 ./mipsevm/tests" \
| parallel -j 8 {} | parallel -j 8 {}
.PHONY: \ .PHONY: \
......
...@@ -25,6 +25,10 @@ func setWitnessField(witness StateWitness, fieldOffset int, fieldData []byte) { ...@@ -25,6 +25,10 @@ func setWitnessField(witness StateWitness, fieldOffset int, fieldData []byte) {
copy(witness[start:end], fieldData) copy(witness[start:end], fieldData)
} }
func setWitnessWord(witness StateWitness, fieldOffset int, value arch.Word) {
arch.ByteOrderWord.PutWord(witness[fieldOffset:], value)
}
// Run through all permutations of `exited` / `exitCode` and ensure that the // Run through all permutations of `exited` / `exitCode` and ensure that the
// correct witness, state hash, and VM Status is produced. // correct witness, state hash, and VM Status is produced.
func TestState_EncodeWitness(t *testing.T) { func TestState_EncodeWitness(t *testing.T) {
...@@ -70,27 +74,27 @@ func TestState_EncodeWitness(t *testing.T) { ...@@ -70,27 +74,27 @@ func TestState_EncodeWitness(t *testing.T) {
expectedWitness := make(StateWitness, STATE_WITNESS_SIZE) expectedWitness := make(StateWitness, STATE_WITNESS_SIZE)
setWitnessField(expectedWitness, MEMROOT_WITNESS_OFFSET, memRoot[:]) setWitnessField(expectedWitness, MEMROOT_WITNESS_OFFSET, memRoot[:])
setWitnessField(expectedWitness, PREIMAGE_KEY_WITNESS_OFFSET, preimageKey[:]) setWitnessField(expectedWitness, PREIMAGE_KEY_WITNESS_OFFSET, preimageKey[:])
setWitnessField(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, []byte{0, 0, 0, byte(preimageOffset)}) setWitnessWord(expectedWitness, PREIMAGE_OFFSET_WITNESS_OFFSET, preimageOffset)
setWitnessField(expectedWitness, HEAP_WITNESS_OFFSET, []byte{0, 0, 0, byte(heap)}) setWitnessWord(expectedWitness, HEAP_WITNESS_OFFSET, heap)
setWitnessField(expectedWitness, LL_RESERVATION_ACTIVE_OFFSET, []byte{1}) setWitnessField(expectedWitness, LL_RESERVATION_ACTIVE_OFFSET, []byte{1})
setWitnessField(expectedWitness, LL_ADDRESS_OFFSET, []byte{0, 0, 0, byte(llAddress)}) setWitnessWord(expectedWitness, LL_ADDRESS_OFFSET, llAddress)
setWitnessField(expectedWitness, LL_OWNER_THREAD_OFFSET, []byte{0, 0, 0, byte(llThreadOwner)}) setWitnessWord(expectedWitness, LL_OWNER_THREAD_OFFSET, llThreadOwner)
setWitnessField(expectedWitness, EXITCODE_WITNESS_OFFSET, []byte{c.exitCode}) setWitnessField(expectedWitness, EXITCODE_WITNESS_OFFSET, []byte{c.exitCode})
if c.exited { if c.exited {
setWitnessField(expectedWitness, EXITED_WITNESS_OFFSET, []byte{1}) setWitnessField(expectedWitness, EXITED_WITNESS_OFFSET, []byte{1})
} }
setWitnessField(expectedWitness, STEP_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(step)}) setWitnessField(expectedWitness, STEP_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(step)})
setWitnessField(expectedWitness, STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(stepsSinceContextSwitch)}) setWitnessField(expectedWitness, STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET, []byte{0, 0, 0, 0, 0, 0, 0, byte(stepsSinceContextSwitch)})
setWitnessField(expectedWitness, WAKEUP_WITNESS_OFFSET, []byte{0xFF, 0xFF, 0xFF, 0xFF}) setWitnessWord(expectedWitness, WAKEUP_WITNESS_OFFSET, ^arch.Word(0))
setWitnessField(expectedWitness, TRAVERSE_RIGHT_WITNESS_OFFSET, []byte{0}) setWitnessField(expectedWitness, TRAVERSE_RIGHT_WITNESS_OFFSET, []byte{0})
setWitnessField(expectedWitness, LEFT_THREADS_ROOT_WITNESS_OFFSET, leftStackRoot[:]) setWitnessField(expectedWitness, LEFT_THREADS_ROOT_WITNESS_OFFSET, leftStackRoot[:])
setWitnessField(expectedWitness, RIGHT_THREADS_ROOT_WITNESS_OFFSET, rightStackRoot[:]) setWitnessField(expectedWitness, RIGHT_THREADS_ROOT_WITNESS_OFFSET, rightStackRoot[:])
setWitnessField(expectedWitness, THREAD_ID_WITNESS_OFFSET, []byte{0, 0, 0, 1}) setWitnessWord(expectedWitness, THREAD_ID_WITNESS_OFFSET, 1)
// Validate witness // Validate witness
actualWitness, actualStateHash := state.EncodeWitness() actualWitness, actualStateHash := state.EncodeWitness()
require.Equal(t, len(actualWitness), STATE_WITNESS_SIZE, "Incorrect witness size") require.Equal(t, len(actualWitness), STATE_WITNESS_SIZE, "Incorrect witness size")
require.EqualValues(t, expectedWitness[:], actualWitness[:], "Incorrect witness") require.EqualValues(t, expectedWitness[:], actualWitness[:], "Incorrect witness: \n\tactual = \t0x%x \n\texpected = \t0x%x", actualWitness, expectedWitness)
// Validate witness hash // Validate witness hash
expectedStateHash := crypto.Keccak256Hash(actualWitness) expectedStateHash := crypto.Keccak256Hash(actualWitness)
expectedStateHash[0] = mipsevm.VmStatus(c.exited, c.exitCode) expectedStateHash[0] = mipsevm.VmStatus(c.exited, c.exitCode)
......
//go:build !cannon64
// +build !cannon64
package singlethreaded package singlethreaded
import ( import (
......
//go:build !cannon64
// +build !cannon64
package singlethreaded package singlethreaded
import ( import (
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -37,6 +36,7 @@ func TestEVM(t *testing.T) { ...@@ -37,6 +36,7 @@ func TestEVM(t *testing.T) {
for _, f := range testFiles { for _, f := range testFiles {
testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name) testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
for _, skipped := range skipped { for _, skipped := range skipped {
if f.Name() == skipped { if f.Name() == skipped {
t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name()) t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name())
...@@ -47,10 +47,7 @@ func TestEVM(t *testing.T) { ...@@ -47,10 +47,7 @@ func TestEVM(t *testing.T) {
// Short-circuit early for exit_group.bin // Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin" exitGroup := f.Name() == "exit_group.bin"
expectPanic := strings.HasSuffix(f.Name(), "panic.bin") expectPanic := strings.HasSuffix(f.Name(), "panic.bin")
validator := testutil.NewEvmValidator(t, c.StateHashFn, c.Contracts, testutil.WithLocalOracle(oracle))
evm := testutil.NewMIPSEVM(c.Contracts)
evm.SetLocalOracle(oracle)
testutil.LogStepFailureAtCleanup(t, evm)
fn := path.Join("open_mips_tests/test/bin", f.Name()) fn := path.Join("open_mips_tests/test/bin", f.Name())
programMem, err := os.ReadFile(fn) programMem, err := os.ReadFile(fn)
...@@ -88,12 +85,7 @@ func TestEVM(t *testing.T) { ...@@ -88,12 +85,7 @@ func TestEVM(t *testing.T) {
stepWitness, err := goVm.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, c.StateHashFn) validator.ValidateEVM(t, stepWitness, curStep, goVm)
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
goPost, _ := goVm.GetState().EncodeWitness()
require.Equalf(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM at step %d", state.GetStep())
} }
if exitGroup { if exitGroup {
require.NotEqual(t, arch.Word(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end")
...@@ -215,6 +207,7 @@ func TestEVMSingleStep_Operators(t *testing.T) { ...@@ -215,6 +207,7 @@ func TestEVMSingleStep_Operators(t *testing.T) {
for i, tt := range cases { for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState() state := goVm.GetState()
var insn uint32 var insn uint32
...@@ -316,6 +309,7 @@ func TestEVMSingleStep_LoadStore(t *testing.T) { ...@@ -316,6 +309,7 @@ func TestEVMSingleStep_LoadStore(t *testing.T) {
for _, v := range versions { for _, v := range versions {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
addr := tt.base + Word(tt.imm) addr := tt.base + Word(tt.imm)
effAddr := arch.AddressMask & addr effAddr := arch.AddressMask & addr
...@@ -490,6 +484,7 @@ func TestEVMSingleStep_MulDiv(t *testing.T) { ...@@ -490,6 +484,7 @@ func TestEVMSingleStep_MulDiv(t *testing.T) {
for i, tt := range cases { for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState() state := goVm.GetState()
var insn uint32 var insn uint32
...@@ -864,6 +859,7 @@ func TestEVMFault(t *testing.T) { ...@@ -864,6 +859,7 @@ func TestEVMFault(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC))
state := goVm.GetState() state := goVm.GetState()
testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) testutil.StoreInstruction(state.GetMemory(), 0, tt.insn)
...@@ -886,8 +882,7 @@ func TestHelloEVM(t *testing.T) { ...@@ -886,8 +882,7 @@ func TestHelloEVM(t *testing.T) {
v := v v := v
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
evm := testutil.NewMIPSEVM(v.Contracts) validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts)
testutil.LogStepFailureAtCleanup(t, evm)
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
elfFile := testutil.ProgramPath("hello") elfFile := testutil.ProgramPath("hello")
...@@ -907,12 +902,7 @@ func TestHelloEVM(t *testing.T) { ...@@ -907,12 +902,7 @@ func TestHelloEVM(t *testing.T) {
stepWitness, err := goVm.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, step, v.StateHashFn) validator.ValidateEVM(t, stepWitness, step, goVm)
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
goPost, _ := goVm.GetState().EncodeWitness()
require.Equalf(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM. insn: %x", insn)
} }
end := time.Now() end := time.Now()
delta := end.Sub(start) delta := end.Sub(start)
...@@ -935,9 +925,7 @@ func TestClaimEVM(t *testing.T) { ...@@ -935,9 +925,7 @@ func TestClaimEVM(t *testing.T) {
v := v v := v
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
evm := testutil.NewMIPSEVM(v.Contracts) validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts)
testutil.LogStepFailureAtCleanup(t, evm)
oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t) oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t)
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
...@@ -958,12 +946,7 @@ func TestClaimEVM(t *testing.T) { ...@@ -958,12 +946,7 @@ func TestClaimEVM(t *testing.T) {
stepWitness, err := goVm.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
validator.ValidateEVM(t, stepWitness, curStep, goVm)
evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
} }
require.True(t, state.GetExited(), "must complete program") require.True(t, state.GetExited(), "must complete program")
...@@ -983,8 +966,7 @@ func TestEntryEVM(t *testing.T) { ...@@ -983,8 +966,7 @@ func TestEntryEVM(t *testing.T) {
v := v v := v
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
evm := testutil.NewMIPSEVM(v.Contracts) validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts)
testutil.LogStepFailureAtCleanup(t, evm)
var stdOutBuf, stdErrBuf bytes.Buffer var stdOutBuf, stdErrBuf bytes.Buffer
elfFile := testutil.ProgramPath("entry") elfFile := testutil.ProgramPath("entry")
...@@ -992,7 +974,7 @@ func TestEntryEVM(t *testing.T) { ...@@ -992,7 +974,7 @@ func TestEntryEVM(t *testing.T) {
state := goVm.GetState() state := goVm.GetState()
start := time.Now() start := time.Now()
for i := 0; i < 400_000; i++ { for i := 0; i < 500_000; i++ {
curStep := goVm.GetState().GetStep() curStep := goVm.GetState().GetStep()
if goVm.GetState().GetExited() { if goVm.GetState().GetExited() {
break break
...@@ -1004,11 +986,7 @@ func TestEntryEVM(t *testing.T) { ...@@ -1004,11 +986,7 @@ func TestEntryEVM(t *testing.T) {
stepWitness, err := goVm.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn) validator.ValidateEVM(t, stepWitness, curStep, goVm)
// verify the post-state matches.
goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
} }
end := time.Now() end := time.Now()
delta := end.Sub(start) delta := end.Sub(start)
...@@ -1090,6 +1068,7 @@ func TestEVMSingleStepBranch(t *testing.T) { ...@@ -1090,6 +1068,7 @@ func TestEVMSingleStepBranch(t *testing.T) {
for i, tt := range cases { for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc))
state := goVm.GetState() state := goVm.GetState()
const rsReg = 8 // t0 const rsReg = 8 // t0
......
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -243,6 +242,7 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) { ...@@ -243,6 +242,7 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) {
for _, v := range llVariations { for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name) tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
effAddr := arch.AddressMask & c.addr effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue) oracle := testutil.StaticOracle(t, preimageValue)
...@@ -339,6 +339,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { ...@@ -339,6 +339,7 @@ func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) {
for _, v := range llVariations { for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name) tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) { t.Run(tName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08)) goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08))
step := state.GetStep() step := state.GetStep()
...@@ -409,31 +410,25 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) { ...@@ -409,31 +410,25 @@ func TestEVM_SysClone_FlagHandling(t *testing.T) {
var err error var err error
var stepWitness *mipsevm.StepWitness var stepWitness *mipsevm.StepWitness
us := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil, nil) goVm := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil, nil)
if !c.valid { if !c.valid {
// The VM should exit // The VM should exit
stepWitness, err = us.Step(true) stepWitness, err = goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, curStep+1, state.GetStep()) require.Equal(t, curStep+1, state.GetStep())
require.Equal(t, true, us.GetState().GetExited()) require.Equal(t, true, goVm.GetState().GetExited())
require.Equal(t, uint8(mipsevm.VMStatusPanic), us.GetState().GetExitCode()) require.Equal(t, uint8(mipsevm.VMStatusPanic), goVm.GetState().GetExitCode())
require.Equal(t, 1, state.ThreadCount()) require.Equal(t, 1, state.ThreadCount())
} else { } else {
stepWitness, err = us.Step(true) stepWitness, err = goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, curStep+1, state.GetStep()) require.Equal(t, curStep+1, state.GetStep())
require.Equal(t, false, us.GetState().GetExited()) require.Equal(t, false, goVm.GetState().GetExited())
require.Equal(t, uint8(0), us.GetState().GetExitCode()) require.Equal(t, uint8(0), goVm.GetState().GetExitCode())
require.Equal(t, 2, state.ThreadCount()) require.Equal(t, 2, state.ThreadCount())
} }
evm := testutil.NewMIPSEVM(contracts) testutil.ValidateEVM(t, stepWitness, curStep, goVm, multithreaded.GetStateHashFn(), contracts)
testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, curStep, multithreaded.GetStateHashFn())
goPost, _ := us.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}) })
} }
} }
...@@ -660,6 +655,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -660,6 +655,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
for i, c := range cases { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1234, nil) goVm, state, contracts := setup(t, i*1234, nil)
step := state.GetStep() step := state.GetStep()
...@@ -729,6 +725,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { ...@@ -729,6 +725,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
for i, c := range cases { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1122, nil) goVm, state, contracts := setup(t, i*1122, nil)
mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount) mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount)
step := state.Step step := state.Step
...@@ -1109,6 +1106,7 @@ var NoopSyscalls = map[string]uint32{ ...@@ -1109,6 +1106,7 @@ var NoopSyscalls = map[string]uint32{
func TestEVM_NoopSyscall(t *testing.T) { func TestEVM_NoopSyscall(t *testing.T) {
for noopName, noopVal := range NoopSyscalls { for noopName, noopVal := range NoopSyscalls {
t.Run(noopName, func(t *testing.T) { t.Run(noopName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, int(noopVal), nil) goVm, state, contracts := setup(t, int(noopVal), nil)
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
...@@ -1155,6 +1153,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) { ...@@ -1155,6 +1153,7 @@ func TestEVM_UnsupportedSyscall(t *testing.T) {
i := i i := i
syscallNum := syscallNum syscallNum := syscallNum
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
t.Parallel() t.Parallel()
goVm, state, contracts := setup(t, i*3434, nil) goVm, state, contracts := setup(t, i*3434, nil)
// Setup basic getThreadId syscall instruction // Setup basic getThreadId syscall instruction
...@@ -1196,7 +1195,7 @@ func TestEVM_EmptyThreadStacks(t *testing.T) { ...@@ -1196,7 +1195,7 @@ func TestEVM_EmptyThreadStacks(t *testing.T) {
require.PanicsWithValue(t, "Active thread stack is empty", func() { _, _ = goVm.Step(false) }) require.PanicsWithValue(t, "Active thread stack is empty", func() { _, _ = goVm.Step(false) })
errorMessage := "MIPS2: active thread stack is empty" errorMessage := "active thread stack is empty"
testutil.AssertEVMReverts(t, state, contracts, tracer, proofCase.Proof, testutil.CreateErrorStringMatcher(errorMessage)) testutil.AssertEVMReverts(t, state, contracts, tracer, proofCase.Proof, testutil.CreateErrorStringMatcher(errorMessage))
}) })
} }
......
//go:build !cannon64
// +build !cannon64
package tests package tests
import ( import (
......
...@@ -19,11 +19,20 @@ import ( ...@@ -19,11 +19,20 @@ import (
const syscallInsn = uint32(0x00_00_00_0c) const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk(f *testing.F) { func FuzzStateSyscallBrk32(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func FuzzStateSyscallBrk64(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func doFuzzStateSyscallBrk(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, seed int64) { f.Fuzz(func(t *testing.T, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
state.GetRegistersRef()[2] = arch.SysBrk state.GetRegistersRef()[2] = arch.SysBrk
...@@ -48,7 +57,15 @@ func FuzzStateSyscallBrk(f *testing.F) { ...@@ -48,7 +57,15 @@ func FuzzStateSyscallBrk(f *testing.F) {
}) })
} }
func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallMmap32(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func FuzzStateSyscallMmap64(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func doFuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation // Add special cases for large memory allocation
f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1)) f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1))
f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2))
...@@ -59,6 +76,7 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -59,6 +76,7 @@ func FuzzStateSyscallMmap(f *testing.F) {
f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed), testutil.WithHeap(heap)) testutil.WithRandomization(seed), testutil.WithHeap(heap))
state := goVm.GetState() state := goVm.GetState()
...@@ -103,11 +121,20 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -103,11 +121,20 @@ func FuzzStateSyscallMmap(f *testing.F) {
}) })
} }
func FuzzStateSyscallExitGroup(f *testing.F) { func FuzzStateSyscallExitGroup32(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func FuzzStateSyscallExitGroup64(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func doFuzzStateSyscallExitGroup(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) { f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
...@@ -132,11 +159,20 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ...@@ -132,11 +159,20 @@ func FuzzStateSyscallExitGroup(f *testing.F) {
}) })
} }
func FuzzStateSyscallFcntl(f *testing.F) { func FuzzStateSyscallFcntl32(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func FuzzStateSyscallFcntl64(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func doFuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) { f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
...@@ -188,11 +224,20 @@ func FuzzStateSyscallFcntl(f *testing.F) { ...@@ -188,11 +224,20 @@ func FuzzStateSyscallFcntl(f *testing.F) {
}) })
} }
func FuzzStateHintRead(f *testing.F) { func FuzzStateHintRead32(f *testing.F) {
doFuzzStateHintRead(f)
}
func FuzzStateHintRead64(f *testing.F) {
doFuzzStateHintRead(f)
}
func doFuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
preimageData := []byte("hello world") preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
...@@ -225,14 +270,22 @@ func FuzzStateHintRead(f *testing.F) { ...@@ -225,14 +270,22 @@ func FuzzStateHintRead(f *testing.F) {
}) })
} }
func FuzzStatePreimageRead(f *testing.F) { func FuzzStatePreimageRead32(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func FuzzStatePreimageRead64(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func doFuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
effAddr := addr & arch.AddressMask effAddr := addr & arch.AddressMask
pc = pc & arch.AddressMask pc = pc & arch.AddressMask
preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF} preexistingMemoryVal := ^arch.Word(0)
preimageValue := []byte("hello world") preimageValue := []byte("hello world")
preimageData := testutil.AddPreimageLengthPrefix(preimageValue) preimageData := testutil.AddPreimageLengthPrefix(preimageValue)
if preimageOffset >= Word(len(preimageData)) || pc == effAddr { if preimageOffset >= Word(len(preimageData)) || pc == effAddr {
...@@ -249,11 +302,11 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -249,11 +302,11 @@ func FuzzStatePreimageRead(f *testing.F) {
state.GetRegistersRef()[5] = addr state.GetRegistersRef()[5] = addr
state.GetRegistersRef()[6] = count state.GetRegistersRef()[6] = count
testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn) testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn)
state.GetMemory().SetWord(effAddr, arch.ByteOrderWord.Word(preexistingMemoryVal[:])) state.GetMemory().SetWord(effAddr, preexistingMemoryVal)
step := state.GetStep() step := state.GetStep()
alignment := addr & arch.ExtMask alignment := addr & arch.ExtMask
writeLen := 4 - alignment writeLen := arch.WordSizeBytes - alignment
if count < writeLen { if count < writeLen {
writeLen = count writeLen = count
} }
...@@ -272,7 +325,8 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -272,7 +325,8 @@ func FuzzStatePreimageRead(f *testing.F) {
expected.PreimageOffset += writeLen expected.PreimageOffset += writeLen
if writeLen > 0 { if writeLen > 0 {
// Expect a memory write // Expect a memory write
expectedMemory := preexistingMemoryVal var expectedMemory []byte
expectedMemory = arch.ByteOrderWord.AppendWord(expectedMemory, preexistingMemoryVal)
copy(expectedMemory[alignment:], preimageData[preimageOffset:preimageOffset+writeLen]) copy(expectedMemory[alignment:], preimageData[preimageOffset:preimageOffset+writeLen])
expected.ExpectMemoryWriteWord(effAddr, arch.ByteOrderWord.Word(expectedMemory[:])) expected.ExpectMemoryWriteWord(effAddr, arch.ByteOrderWord.Word(expectedMemory[:]))
} }
...@@ -288,11 +342,20 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -288,11 +342,20 @@ func FuzzStatePreimageRead(f *testing.F) {
}) })
} }
func FuzzStateHintWrite(f *testing.F) { func FuzzStateHintWrite32(f *testing.F) {
doFuzzStateHintWrite(f)
}
func FuzzStateHintWrite64(f *testing.F) {
doFuzzStateHintWrite(f)
}
func doFuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with hint data in memory // Make sure pc does not overlap with hint data in memory
pc := Word(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
...@@ -370,11 +433,20 @@ func FuzzStateHintWrite(f *testing.F) { ...@@ -370,11 +433,20 @@ func FuzzStateHintWrite(f *testing.F) {
}) })
} }
func FuzzStatePreimageWrite(f *testing.F) { func FuzzStatePreimageWrite32(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func FuzzStatePreimageWrite64(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func doFuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with preimage data in memory // Make sure pc does not overlap with preimage data in memory
pc := Word(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
......
...@@ -13,9 +13,18 @@ import ( ...@@ -13,9 +13,18 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
func FuzzStateSyscallCloneMT(f *testing.F) { func FuzzStateSyscallCloneMT32(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func FuzzStateSyscallCloneMT64(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func doFuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f) v := GetMultiThreadedTestCase(f)
f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) { f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := mttestutil.GetMtState(t, goVm) state := mttestutil.GetMtState(t, goVm)
// Update existing threads to avoid collision with nextThreadId // Update existing threads to avoid collision with nextThreadId
......
//go:build !cannon64
// +build !cannon64
package tests package tests
import ( import (
"os" "os"
"testing" "testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
...@@ -32,11 +34,6 @@ func FuzzStateSyscallCloneST(f *testing.F) { ...@@ -32,11 +34,6 @@ func FuzzStateSyscallCloneST(f *testing.F) {
require.False(t, stepWitness.HasPreimage()) require.False(t, stepWitness.HasPreimage())
expected.Validate(t, state) expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
evm := testutil.NewMIPSEVM(v.Contracts)
evmPost := evm.Step(t, stepWitness, step, v.StateHashFn)
goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}) })
} }
...@@ -40,7 +40,7 @@ type MIPSEVM struct { ...@@ -40,7 +40,7 @@ type MIPSEVM struct {
lastPreimageOracleInput []byte lastPreimageOracleInput []byte
} }
func NewMIPSEVM(contracts *ContractMetadata, opts ...evmOption) *MIPSEVM { func newMIPSEVM(contracts *ContractMetadata, opts ...evmOption) *MIPSEVM {
env, evmState := NewEVMEnv(contracts) env, evmState := NewEVMEnv(contracts)
sender := vm.AccountRef{0x13, 0x37} sender := vm.AccountRef{0x13, 0x37}
startingGas := uint64(maxStepGas) startingGas := uint64(maxStepGas)
...@@ -65,6 +65,12 @@ func WithTracingHooks(tracer *tracing.Hooks) evmOption { ...@@ -65,6 +65,12 @@ func WithTracingHooks(tracer *tracing.Hooks) evmOption {
} }
} }
func WithLocalOracle(oracle mipsevm.PreimageOracle) evmOption {
return func(evm *MIPSEVM) {
evm.SetLocalOracle(oracle)
}
}
func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) { func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) {
m.env.Config.Tracer = tracer m.env.Config.Tracer = tracer
} }
...@@ -192,17 +198,35 @@ func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) { ...@@ -192,17 +198,35 @@ func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) {
}) })
} }
// ValidateEVM runs a single evm step and validates against an FPVM poststate type EvmValidator struct {
func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) { evm *MIPSEVM
evm := NewMIPSEVM(contracts, opts...) hashFn mipsevm.HashFn
}
// NewEvmValidator creates a validator that can be run repeatedly across multiple steps
func NewEvmValidator(t *testing.T, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) *EvmValidator {
evm := newMIPSEVM(contracts, opts...)
LogStepFailureAtCleanup(t, evm) LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, step, hashFn) return &EvmValidator{
evm: evm,
hashFn: hashFn,
}
}
func (v *EvmValidator) ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM) {
evmPost := v.evm.Step(t, stepWitness, step, v.hashFn)
goPost, _ := goVm.GetState().EncodeWitness() goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM") "mipsevm produced different state than EVM")
} }
// ValidateEVM runs a single evm step and validates against an FPVM poststate
func ValidateEVM(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, goVm mipsevm.FPVM, hashFn mipsevm.HashFn, contracts *ContractMetadata, opts ...evmOption) {
validator := NewEvmValidator(t, hashFn, contracts)
validator.ValidateEVM(t, stepWitness, step, goVm)
}
type ErrMatcher func(*testing.T, []byte) type ErrMatcher func(*testing.T, []byte)
func CreateNoopErrorMatcher() ErrMatcher { func CreateNoopErrorMatcher() ErrMatcher {
...@@ -250,7 +274,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract ...@@ -250,7 +274,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract
} }
func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, opts ...evmOption) { func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, opts ...evmOption) {
evm := NewMIPSEVM(contracts, opts...) evm := newMIPSEVM(contracts, opts...)
LogStepFailureAtCleanup(t, evm) LogStepFailureAtCleanup(t, evm)
evm.assertPreimageOracleReverts(t, preimageKey, preimageValue, preimageOffset) evm.assertPreimageOracleReverts(t, preimageKey, preimageValue, preimageOffset)
......
...@@ -141,3 +141,10 @@ func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.Create ...@@ -141,3 +141,10 @@ func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.Create
require.Equal(t, expectedStdOut, stdOutBuf.String(), "stdout") require.Equal(t, expectedStdOut, stdOutBuf.String(), "stdout")
require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr") require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr")
} }
func TemporarilySkip64BitTests(t *testing.T) {
if !arch.IsMips32 {
// TODO(#12598) Update and enable these tests
t.Skip("Skipping 64-bit test")
}
}
//go:build cannon64
// +build cannon64
package versions
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/op-service/serialize"
)
func TestNewFromState(t *testing.T) {
t.Run("multithreaded64", func(t *testing.T) {
actual, err := NewFromState(multithreaded.CreateEmptyState())
require.NoError(t, err)
require.IsType(t, &multithreaded.State{}, actual.FPVMState)
require.Equal(t, VersionMultiThreaded64, actual.Version)
})
}
func TestLoadStateFromFile(t *testing.T) {
t.Run("Multithreaded64FromBinary", func(t *testing.T) {
expected, err := NewFromState(multithreaded.CreateEmptyState())
require.NoError(t, err)
path := writeToFile(t, "state.bin.gz", expected)
actual, err := LoadStateFromFile(path)
require.NoError(t, err)
require.Equal(t, expected, actual)
})
}
func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) {
tests := []struct {
version StateVersion
createState func() mipsevm.FPVMState
}{
{VersionMultiThreaded64, func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }},
}
for _, test := range tests {
test := test
t.Run(test.version.String(), func(t *testing.T) {
state, err := NewFromState(test.createState())
require.NoError(t, err)
dir := t.TempDir()
path := filepath.Join(dir, "test.json")
err = serialize.Write(path, state, 0o644)
require.ErrorIs(t, err, ErrJsonNotSupported)
})
}
}
func writeToFile(t *testing.T, filename string, data serialize.Serializable) string {
dir := t.TempDir()
path := filepath.Join(dir, filename)
require.NoError(t, serialize.Write(path, data, 0o644))
return path
}
//go:build !cannon64
// +build !cannon64
package versions package versions
import ( import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/op-service/serialize" "github.com/ethereum-optimism/optimism/op-service/serialize"
"github.com/stretchr/testify/require"
) )
func TestNewFromState(t *testing.T) { func TestNewFromState(t *testing.T) {
...@@ -49,10 +53,6 @@ func TestLoadStateFromFile(t *testing.T) { ...@@ -49,10 +53,6 @@ func TestLoadStateFromFile(t *testing.T) {
}) })
} }
func TestLoadStateFromFile64(t *testing.T) {
t.Skip("TODO(#12205): Test asserting that cannon64 fails to decode a 32-bit state")
}
func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) {
tests := []struct { tests := []struct {
version StateVersion version StateVersion
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment