Commit 5212c4db authored by mbaxter's avatar mbaxter Committed by GitHub

cannon: Run common evm tests across all implementations (#11333)

* cannon: Prep test utils to handle Mips2.sol

* cannon: Add metadata struct to hold all contract-related metadata

* cannon: Add forge debug test for mips2

* cannon: Fix path to mips2 artifacts in testutil

* cannon: Rework evm tests to run across both cannon impls

* cannon: Skip failing test for now, add todo

* cannon: Rename FPVMState.GetRegisters to GetRegistersMutable

* cannon: Run linter

* cannon: Fix skipped claim test

* cannon: Rename FPVMState registers getter to follow convention

* cannon: Rename cpu getter to match naming convention

* cannon: Fix bad merge - elf paths, versioned references
parent 7c83398b
...@@ -29,8 +29,8 @@ test: elf contract ...@@ -29,8 +29,8 @@ test: elf contract
go test -v ./... go test -v ./...
fuzz: fuzz:
# Common vm tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallClone ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests
...@@ -38,6 +38,10 @@ fuzz: ...@@ -38,6 +38,10 @@ fuzz:
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests
# Single-threaded tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests
# Multi-threaded tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests
.PHONY: \ .PHONY: \
cannon \ cannon \
......
...@@ -2,6 +2,7 @@ package mipsevm ...@@ -2,6 +2,7 @@ package mipsevm
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -9,11 +10,23 @@ import ( ...@@ -9,11 +10,23 @@ import (
type FPVMState interface { type FPVMState interface {
GetMemory() *memory.Memory GetMemory() *memory.Memory
// GetHeap returns the current memory address at the top of the heap
GetHeap() uint32
// GetPreimageKey returns the most recently accessed preimage key
GetPreimageKey() common.Hash
// GetPreimageOffset returns the current offset into the current preimage
GetPreimageOffset() uint32
// GetPC returns the currently executing program counter // GetPC returns the currently executing program counter
GetPC() uint32 GetPC() uint32
// GetRegisters returns the currently active registers // GetCpu returns the currently active cpu scalars, including the program counter
GetRegisters() *[32]uint32 GetCpu() CpuScalars
// GetRegistersRef returns a pointer to the currently active registers
GetRegistersRef() *[32]uint32
// GetStep returns the current VM step // GetStep returns the current VM step
GetStep() uint64 GetStep() uint64
...@@ -24,6 +37,16 @@ type FPVMState interface { ...@@ -24,6 +37,16 @@ type FPVMState interface {
// GetExitCode returns the exit code // GetExitCode returns the exit code
GetExitCode() uint8 GetExitCode() uint8
// GetLastHint returns optional metadata which is not part of the VM state itself.
// It is used to remember the last pre-image hint,
// so a VM can start from any state without fetching prior pre-images,
// and instead just repeat the last hint on setup,
// to make sure pre-image requests can be served.
// The first 4 bytes are a uin32 length prefix.
// Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered,
// and should only be read when len(LastHint) > 4 && uint32(LastHint[:4]) <= len(LastHint[4:])
GetLastHint() hexutil.Bytes
// EncodeWitness returns the witness for the current state and the state hash // EncodeWitness returns the witness for the current state and the state hash
EncodeWitness() (witness []byte, hash common.Hash) EncodeWitness() (witness []byte, hash common.Hash)
} }
......
...@@ -14,9 +14,9 @@ import ( ...@@ -14,9 +14,9 @@ import (
) )
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
thread := m.state.getCurrentThread() thread := m.state.GetCurrentThread()
syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegisters()) syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegistersRef())
v0 := uint32(0) v0 := uint32(0)
v1 := uint32(0) v1 := uint32(0)
...@@ -188,7 +188,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -188,7 +188,7 @@ func (m *InstrumentedState) mipsStep() error {
return nil return nil
} }
m.state.Step += 1 m.state.Step += 1
thread := m.state.getCurrentThread() thread := m.state.GetCurrentThread()
// During wakeup traversal, search for the first thread blocked on the wakeup address. // During wakeup traversal, search for the first thread blocked on the wakeup address.
// Don't allow regular execution until we have found such a thread or else we have visited all threads. // Don't allow regular execution until we have found such a thread or else we have visited all threads.
...@@ -264,7 +264,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -264,7 +264,7 @@ func (m *InstrumentedState) mipsStep() error {
} }
// Exec the rest of the step logic // Exec the rest of the step logic
return exec.ExecMipsCoreStepLogic(m.state.getCpu(), m.state.GetRegisters(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) return exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker)
} }
func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) { func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) {
......
...@@ -53,7 +53,7 @@ func (t *ThreadedStackTrackerImpl) Traceback() { ...@@ -53,7 +53,7 @@ func (t *ThreadedStackTrackerImpl) Traceback() {
} }
func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracker { func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracker {
thread := t.state.getCurrentThread() thread := t.state.GetCurrentThread()
tracker, exists := t.trackersByThreadId[thread.ThreadId] tracker, exists := t.trackersByThreadId[thread.ThreadId]
if !exists { if !exists {
tracker = exec.NewStackTrackerUnsafe(t.state, t.meta) tracker = exec.NewStackTrackerUnsafe(t.state, t.meta)
......
...@@ -52,13 +52,6 @@ type State struct { ...@@ -52,13 +52,6 @@ type State struct {
NextThreadId uint32 `json:"nextThreadId"` NextThreadId uint32 `json:"nextThreadId"`
// LastHint is optional metadata, and not part of the VM state itself. // LastHint is optional metadata, and not part of the VM state itself.
// It is used to remember the last pre-image hint,
// so a VM can start from any state without fetching prior pre-images,
// and instead just repeat the last hint on setup,
// to make sure pre-image requests can be served.
// The first 4 bytes are a uin32 length prefix.
// Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered,
// and should only be read when len(LastHint) > 4 && uint32(LastHint[:4]) <= len(LastHint[4:])
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
} }
...@@ -83,7 +76,7 @@ func CreateEmptyState() *State { ...@@ -83,7 +76,7 @@ func CreateEmptyState() *State {
func CreateInitialState(pc, heapStart uint32) *State { func CreateInitialState(pc, heapStart uint32) *State {
state := CreateEmptyState() state := CreateEmptyState()
currentThread := state.getCurrentThread() currentThread := state.GetCurrentThread()
currentThread.Cpu.PC = pc currentThread.Cpu.PC = pc
currentThread.Cpu.NextPC = pc + 4 currentThread.Cpu.NextPC = pc + 4
state.Heap = heapStart state.Heap = heapStart
...@@ -91,7 +84,7 @@ func CreateInitialState(pc, heapStart uint32) *State { ...@@ -91,7 +84,7 @@ func CreateInitialState(pc, heapStart uint32) *State {
return state return state
} }
func (s *State) getCurrentThread() *ThreadState { func (s *State) GetCurrentThread() *ThreadState {
activeStack := s.getActiveThreadStack() activeStack := s.getActiveThreadStack()
activeStackSize := len(activeStack) activeStackSize := len(activeStack)
...@@ -131,17 +124,22 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash { ...@@ -131,17 +124,22 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash {
} }
func (s *State) GetPC() uint32 { func (s *State) GetPC() uint32 {
activeThread := s.getCurrentThread() activeThread := s.GetCurrentThread()
return activeThread.Cpu.PC return activeThread.Cpu.PC
} }
func (s *State) GetRegisters() *[32]uint32 { func (s *State) GetCpu() mipsevm.CpuScalars {
activeThread := s.getCurrentThread() activeThread := s.GetCurrentThread()
return &activeThread.Registers return activeThread.Cpu
}
func (s *State) getCpuRef() *mipsevm.CpuScalars {
return &s.GetCurrentThread().Cpu
} }
func (s *State) getCpu() *mipsevm.CpuScalars { func (s *State) GetRegistersRef() *[32]uint32 {
return &s.getCurrentThread().Cpu activeThread := s.GetCurrentThread()
return &activeThread.Registers
} }
func (s *State) GetExitCode() uint8 { return s.ExitCode } func (s *State) GetExitCode() uint8 { return s.ExitCode }
...@@ -150,6 +148,10 @@ func (s *State) GetExited() bool { return s.Exited } ...@@ -150,6 +148,10 @@ func (s *State) GetExited() bool { return s.Exited }
func (s *State) GetStep() uint64 { return s.Step } func (s *State) GetStep() uint64 { return s.Step }
func (s *State) GetLastHint() hexutil.Bytes {
return s.LastHint
}
func (s *State) VMStatus() uint8 { func (s *State) VMStatus() uint8 {
return mipsevm.VmStatus(s.Exited, s.ExitCode) return mipsevm.VmStatus(s.Exited, s.ExitCode)
} }
...@@ -158,6 +160,18 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -158,6 +160,18 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 {
return s.Heap
}
func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey
}
func (s *State) GetPreimageOffset() uint32 {
return s.PreimageOffset
}
func (s *State) EncodeWitness() ([]byte, common.Hash) { func (s *State) EncodeWitness() ([]byte, common.Hash) {
out := make([]byte, 0, STATE_WITNESS_SIZE) out := make([]byte, 0, STATE_WITNESS_SIZE)
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
...@@ -214,6 +228,12 @@ func (sw StateWitness) StateHash() (common.Hash, error) { ...@@ -214,6 +228,12 @@ func (sw StateWitness) StateHash() (common.Hash, error) {
return stateHashFromWitness(sw), nil return stateHashFromWitness(sw), nil
} }
func GetStateHashFn() mipsevm.HashFn {
return func(sw []byte) (common.Hash, error) {
return StateWitness(sw).StateHash()
}
}
func stateHashFromWitness(sw []byte) common.Hash { func stateHashFromWitness(sw []byte) common.Hash {
if len(sw) != STATE_WITNESS_SIZE { if len(sw) != STATE_WITNESS_SIZE {
panic("Invalid witness length") panic("Invalid witness length")
......
...@@ -133,7 +133,7 @@ func TestState_EmptyThreadsRoot(t *testing.T) { ...@@ -133,7 +133,7 @@ func TestState_EmptyThreadsRoot(t *testing.T) {
func TestState_EncodeThreadProof_SingleThread(t *testing.T) { func TestState_EncodeThreadProof_SingleThread(t *testing.T) {
state := CreateEmptyState() state := CreateEmptyState()
// Set some fields on the active thread // Set some fields on the active thread
activeThread := state.getCurrentThread() activeThread := state.GetCurrentThread()
activeThread.Cpu.PC = 4 activeThread.Cpu.PC = 4
activeThread.Cpu.NextPC = 8 activeThread.Cpu.NextPC = 8
activeThread.Cpu.HI = 11 activeThread.Cpu.HI = 11
...@@ -181,7 +181,7 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { ...@@ -181,7 +181,7 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
expectedRoot = crypto.Keccak256Hash(hashData) expectedRoot = crypto.Keccak256Hash(hashData)
} }
expectedProof := append([]byte{}, state.getCurrentThread().serializeThread()[:]...) expectedProof := append([]byte{}, state.GetCurrentThread().serializeThread()[:]...)
expectedProof = append(expectedProof, expectedRoot[:]...) expectedProof = append(expectedProof, expectedRoot[:]...)
actualProof := state.EncodeThreadProof() actualProof := state.EncodeThreadProof()
......
...@@ -64,7 +64,7 @@ func PatchStack(st mipsevm.FPVMState) error { ...@@ -64,7 +64,7 @@ func PatchStack(st mipsevm.FPVMState) error {
if err := st.GetMemory().SetMemoryRange(sp-4*memory.PageSize, bytes.NewReader(make([]byte, 5*memory.PageSize))); err != nil { if err := st.GetMemory().SetMemoryRange(sp-4*memory.PageSize, bytes.NewReader(make([]byte, 5*memory.PageSize))); err != nil {
return fmt.Errorf("failed to allocate page for stack content") return fmt.Errorf("failed to allocate page for stack content")
} }
st.GetRegisters()[29] = sp st.GetRegistersRef()[29] = sp
storeMem := func(addr uint32, v uint32) { storeMem := func(addr uint32, v uint32) {
var dat [4]byte var dat [4]byte
......
...@@ -34,13 +34,6 @@ type State struct { ...@@ -34,13 +34,6 @@ type State struct {
Registers [32]uint32 `json:"registers"` Registers [32]uint32 `json:"registers"`
// LastHint is optional metadata, and not part of the VM state itself. // LastHint is optional metadata, and not part of the VM state itself.
// It is used to remember the last pre-image hint,
// so a VM can start from any state without fetching prior pre-images,
// and instead just repeat the last hint on setup,
// to make sure pre-image requests can be served.
// The first 4 bytes are a uin32 length prefix.
// Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered,
// and should only be read when len(LastHint) > 4 && uint32(LastHint[:4]) <= len(LastHint[4:])
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
} }
...@@ -130,7 +123,9 @@ func (s *State) UnmarshalJSON(data []byte) error { ...@@ -130,7 +123,9 @@ func (s *State) UnmarshalJSON(data []byte) error {
func (s *State) GetPC() uint32 { return s.Cpu.PC } func (s *State) GetPC() uint32 { return s.Cpu.PC }
func (s *State) GetRegisters() *[32]uint32 { return &s.Registers } func (s *State) GetCpu() mipsevm.CpuScalars { return s.Cpu }
func (s *State) GetRegistersRef() *[32]uint32 { return &s.Registers }
func (s *State) GetExitCode() uint8 { return s.ExitCode } func (s *State) GetExitCode() uint8 { return s.ExitCode }
...@@ -138,6 +133,10 @@ func (s *State) GetExited() bool { return s.Exited } ...@@ -138,6 +133,10 @@ func (s *State) GetExited() bool { return s.Exited }
func (s *State) GetStep() uint64 { return s.Step } func (s *State) GetStep() uint64 { return s.Step }
func (s *State) GetLastHint() hexutil.Bytes {
return s.LastHint
}
func (s *State) VMStatus() uint8 { func (s *State) VMStatus() uint8 {
return mipsevm.VmStatus(s.Exited, s.ExitCode) return mipsevm.VmStatus(s.Exited, s.ExitCode)
} }
...@@ -146,6 +145,18 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -146,6 +145,18 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 {
return s.Heap
}
func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey
}
func (s *State) GetPreimageOffset() uint32 {
return s.PreimageOffset
}
func (s *State) EncodeWitness() ([]byte, common.Hash) { func (s *State) EncodeWitness() ([]byte, common.Hash) {
out := make([]byte, 0, STATE_WITNESS_SIZE) out := make([]byte, 0, STATE_WITNESS_SIZE)
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
......
...@@ -2,6 +2,7 @@ package tests ...@@ -2,6 +2,7 @@ package tests
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
"path" "path"
...@@ -18,163 +19,109 @@ import ( ...@@ -18,163 +19,109 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
func testContractsSetup(t require.TestingT) (*testutil.Artifacts, *testutil.Addresses) {
artifacts, err := testutil.LoadArtifacts()
require.NoError(t, err)
addrs := &testutil.Addresses{
MIPS: common.Address{0: 0xff, 19: 1},
Oracle: common.Address{0: 0xff, 19: 2},
Sender: common.Address{0x13, 0x37},
FeeRecipient: common.Address{0xaa},
}
return artifacts, addrs
}
func TestEVM(t *testing.T) { func TestEVM(t *testing.T) {
testFiles, err := os.ReadDir("open_mips_tests/test/bin") testFiles, err := os.ReadDir("open_mips_tests/test/bin")
require.NoError(t, err) require.NoError(t, err)
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks // no-tracer by default, but test_util.MarkdownTracer var tracer *tracing.Hooks // no-tracer by default, but test_util.MarkdownTracer
for _, f := range testFiles { cases := GetMipsVersionTestCases(t)
t.Run(f.Name(), func(t *testing.T) { skippedTests := map[string][]string{
oracle := testutil.SelectOracleFixture(t, f.Name()) "multi-threaded": []string{"clone.bin"},
// Short-circuit early for exit_group.bin "single-threaded": []string{},
exitGroup := f.Name() == "exit_group.bin" }
expectPanic := strings.HasSuffix(f.Name(), "panic.bin")
evm := testutil.NewMIPSEVM(contracts, addrs) for _, c := range cases {
evm.SetTracer(tracer) skipped, exists := skippedTests[c.Name]
evm.SetLocalOracle(oracle) require.True(t, exists)
testutil.LogStepFailureAtCleanup(t, evm) for _, f := range testFiles {
testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name)
t.Run(testName, func(t *testing.T) {
for _, skipped := range skipped {
if f.Name() == skipped {
t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name())
}
}
fn := path.Join("open_mips_tests/test/bin", f.Name()) oracle := testutil.SelectOracleFixture(t, f.Name())
programMem, err := os.ReadFile(fn) // Short-circuit early for exit_group.bin
require.NoError(t, err) exitGroup := f.Name() == "exit_group.bin"
state := &singlethreaded.State{Cpu: mipsevm.CpuScalars{PC: 0, NextPC: 4}, Memory: memory.NewMemory()} expectPanic := strings.HasSuffix(f.Name(), "panic.bin")
err = state.Memory.SetMemoryRange(0, bytes.NewReader(programMem))
require.NoError(t, err, "load program into state")
// set the return address ($ra) to jump into when test completes evm := testutil.NewMIPSEVM(c.Contracts)
state.Registers[31] = testutil.EndAddr evm.SetTracer(tracer)
evm.SetLocalOracle(oracle)
testutil.LogStepFailureAtCleanup(t, evm)
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil) fn := path.Join("open_mips_tests/test/bin", f.Name())
programMem, err := os.ReadFile(fn)
require.NoError(t, err)
// Catch panics and check if they are expected goVm := c.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger())
defer func() { state := goVm.GetState()
if r := recover(); r != nil { err = state.GetMemory().SetMemoryRange(0, bytes.NewReader(programMem))
if expectPanic { require.NoError(t, err, "load program into state")
// Success
} else { // set the return address ($ra) to jump into when test completes
t.Errorf("unexpected panic: %v", r) state.GetRegistersRef()[31] = testutil.EndAddr
// Catch panics and check if they are expected
defer func() {
if r := recover(); r != nil {
if expectPanic {
// Success
} else {
t.Errorf("unexpected panic: %v", r)
}
} }
} }()
}()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
curStep := goState.GetState().GetStep() curStep := goVm.GetState().GetStep()
if goState.GetState().GetPC() == testutil.EndAddr { if goVm.GetState().GetPC() == testutil.EndAddr {
break break
}
if exitGroup && goVm.GetState().GetExited() {
break
}
insn := state.GetMemory().GetMemory(state.GetPC())
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, c.StateHashFn)
// 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 && goState.GetState().GetExited() { if exitGroup {
break require.NotEqual(t, uint32(testutil.EndAddr), goVm.GetState().GetPC(), "must not reach end")
require.True(t, goVm.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), goVm.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic {
require.NotEqual(t, uint32(testutil.EndAddr), state.GetPC(), "must not reach end")
} else {
require.Equal(t, uint32(testutil.EndAddr), state.GetPC(), "must reach end")
// inspect test result
done, result := state.GetMemory().GetMemory(testutil.BaseAddrEnd+4), state.GetMemory().GetMemory(testutil.BaseAddrEnd+8)
require.Equal(t, done, uint32(1), "must be done")
require.Equal(t, result, uint32(1), "must have success result")
} }
insn := state.Memory.GetMemory(state.Cpu.PC) })
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.Cpu.PC, insn) }
stepWitness, err := goState.Step(true)
require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn())
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
goPost, _ := goState.GetState().EncodeWitness()
require.Equalf(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM at step %d", state.Step)
}
if exitGroup {
require.NotEqual(t, uint32(testutil.EndAddr), goState.GetState().GetPC(), "must not reach end")
require.True(t, goState.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), goState.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic {
require.NotEqual(t, uint32(testutil.EndAddr), goState.GetState().GetPC(), "must not reach end")
} else {
require.Equal(t, uint32(testutil.EndAddr), state.Cpu.PC, "must reach end")
// inspect test result
done, result := state.Memory.GetMemory(testutil.BaseAddrEnd+4), state.Memory.GetMemory(testutil.BaseAddrEnd+8)
require.Equal(t, done, uint32(1), "must be done")
require.Equal(t, result, uint32(1), "must have success result")
}
})
}
}
func TestEVM_CloneFlags(t *testing.T) {
//contracts, addrs := testContractsSetup(t)
//var tracer *tracing.Hooks
cases := []struct {
name string
flags uint32
valid bool
}{
{"the supported flags bitmask", exec.ValidCloneFlags, true},
{"no flags", 0, false},
{"all flags", ^uint32(0), false},
{"all unsupported flags", ^uint32(exec.ValidCloneFlags), false},
{"a few supported flags", exec.CloneFs | exec.CloneSysvsem, false},
{"one supported flag", exec.CloneFs, false},
{"mixed supported and unsupported flags", exec.CloneFs | exec.CloneParentSettid, false},
{"a single unsupported flag", exec.CloneUntraced, false},
{"multiple unsupported flags", exec.CloneUntraced | exec.CloneParentSettid, false},
}
const insn = uint32(0x00_00_00_0C) // syscall instruction
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
state := multithreaded.CreateEmptyState()
state.Memory.SetMemory(state.GetPC(), insn)
state.GetRegisters()[2] = exec.SysClone // Set syscall number
state.GetRegisters()[4] = tt.flags // Set first argument
//curStep := state.Step
us := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
if !tt.valid {
// The VM should exit
_, err := us.Step(true)
require.NoError(t, err)
require.Equal(t, true, us.GetState().GetExited())
require.Equal(t, uint8(mipsevm.VMStatusPanic), us.GetState().GetExitCode())
} else {
/*stepWitness*/ _, err := us.Step(true)
require.NoError(t, err)
}
// TODO: Validate EVM execution once onchain implementation is ready
//evm := testutil.NewMIPSEVM(contracts, addrs)
//evm.SetTracer(tracer)
//testutil.LogStepFailureAtCleanup(t, evm)
//
//evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn())
//goPost, _ := us.GetState().EncodeWitness()
//require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
// "mipsevm produced different state than EVM")
})
} }
} }
func TestEVMSingleStep(t *testing.T) { func TestEVMSingleStep(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks var tracer *tracing.Hooks
versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
pc uint32 pc uint32
...@@ -187,32 +134,35 @@ func TestEVMSingleStep(t *testing.T) { ...@@ -187,32 +134,35 @@ func TestEVMSingleStep(t *testing.T) {
{"jal non-zero PC region", 0x10000000, 0x10000004, 0x0C_00_00_02}, // jal 0x2 {"jal non-zero PC region", 0x10000000, 0x10000004, 0x0C_00_00_02}, // jal 0x2
} }
for _, tt := range cases { for _, v := range versions {
t.Run(tt.name, func(t *testing.T) { for _, tt := range cases {
state := &singlethreaded.State{Cpu: mipsevm.CpuScalars{PC: tt.pc, NextPC: tt.nextPC}, Memory: memory.NewMemory()} testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
state.Memory.SetMemory(tt.pc, tt.insn) t.Run(testName, func(t *testing.T) {
curStep := state.Step goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), WithPC(tt.pc), WithNextPC(tt.nextPC))
state := goVm.GetState()
state.GetMemory().SetMemory(tt.pc, tt.insn)
curStep := state.GetStep()
us := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil) stepWitness, err := goVm.Step(true)
stepWitness, err := us.Step(true) require.NoError(t, err)
require.NoError(t, err)
evm := testutil.NewMIPSEVM(contracts, addrs) evm := testutil.NewMIPSEVM(v.Contracts)
evm.SetTracer(tracer) evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm) testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn()) evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
goPost, _ := us.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")
}) })
}
} }
} }
func TestEVM_MMap(t *testing.T) { func TestEVM_MMap(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks var tracer *tracing.Hooks
versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
heap uint32 heap uint32
...@@ -232,69 +182,72 @@ func TestEVM_MMap(t *testing.T) { ...@@ -232,69 +182,72 @@ func TestEVM_MMap(t *testing.T) {
{name: "Request specific address", heap: program.HEAP_START, address: 0x50_00_00_00, size: 0, shouldFail: false, expectedHeap: program.HEAP_START}, {name: "Request specific address", heap: program.HEAP_START, address: 0x50_00_00_00, size: 0, shouldFail: false, expectedHeap: program.HEAP_START},
} }
for _, c := range cases { for _, v := range versions {
t.Run(c.name, func(t *testing.T) { for _, c := range cases {
state := singlethreaded.CreateEmptyState() testName := fmt.Sprintf("%v (%v)", c.name, v.Name)
state.Heap = c.heap t.Run(testName, func(t *testing.T) {
state.Memory.SetMemory(state.GetPC(), syscallInsn) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), WithHeap(c.heap))
state.Registers = testutil.RandomRegisters(77) state := goVm.GetState()
state.Registers[2] = exec.SysMmap
state.Registers[4] = c.address state.GetMemory().SetMemory(state.GetPC(), syscallInsn)
state.Registers[5] = c.size *state.GetRegistersRef() = testutil.RandomRegisters(77)
step := state.Step state.GetRegistersRef()[2] = exec.SysMmap
state.GetRegistersRef()[4] = c.address
expectedRegisters := state.Registers state.GetRegistersRef()[5] = c.size
expectedHeap := state.Heap step := state.GetStep()
expectedMemoryRoot := state.Memory.MerkleRoot()
if c.shouldFail { expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = exec.SysErrorSignal expectedHeap := state.GetHeap()
expectedRegisters[7] = exec.MipsEINVAL expectedMemoryRoot := state.GetMemory().MerkleRoot()
} else { if c.shouldFail {
expectedHeap = c.expectedHeap expectedRegisters[2] = exec.SysErrorSignal
if c.address == 0 { expectedRegisters[7] = exec.MipsEINVAL
expectedRegisters[2] = state.Heap
expectedRegisters[7] = 0
} else { } else {
expectedRegisters[2] = c.address expectedHeap = c.expectedHeap
expectedRegisters[7] = 0 if c.address == 0 {
expectedRegisters[2] = state.GetHeap()
expectedRegisters[7] = 0
} else {
expectedRegisters[2] = c.address
expectedRegisters[7] = 0
}
} }
}
us := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil) stepWitness, err := goVm.Step(true)
stepWitness, err := us.Step(true) require.NoError(t, err)
require.NoError(t, err)
// Check expectations
require.Equal(t, step+1, state.Step)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, expectedMemoryRoot, state.Memory.MerkleRoot())
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, false, state.Exited)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, hexutil.Bytes(nil), state.LastHint)
evm := testutil.NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn()) // Check expectations
goPost, _ := us.GetState().EncodeWitness() require.Equal(t, step+1, state.GetStep())
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(), require.Equal(t, expectedHeap, state.GetHeap())
"mipsevm produced different state than EVM") require.Equal(t, expectedRegisters, state.GetRegistersRef())
}) require.Equal(t, expectedMemoryRoot, state.GetMemory().MerkleRoot())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, uint32(0), state.GetPreimageOffset())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, false, state.GetExited())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, hexutil.Bytes(nil), state.GetLastHint())
evm := testutil.NewMIPSEVM(v.Contracts)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
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")
})
}
} }
} }
func TestEVMSysWriteHint(t *testing.T) { func TestEVMSysWriteHint(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks var tracer *tracing.Hooks
versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
memOffset int // Where the hint data is stored in memory memOffset int // Where the hint data is stored in memory
...@@ -439,47 +392,45 @@ func TestEVMSysWriteHint(t *testing.T) { ...@@ -439,47 +392,45 @@ func TestEVMSysWriteHint(t *testing.T) {
insn = uint32(0x00_00_00_0C) // syscall instruction insn = uint32(0x00_00_00_0C) // syscall instruction
) )
for _, tt := range cases { for _, v := range versions {
t.Run(tt.name, func(t *testing.T) { for _, tt := range cases {
oracle := hintTrackingOracle{} testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
state := &singlethreaded.State{Cpu: mipsevm.CpuScalars{PC: 0, NextPC: 4}, Memory: memory.NewMemory()} t.Run(testName, func(t *testing.T) {
oracle := hintTrackingOracle{}
state.LastHint = tt.lastHint goVm := v.VMFactory(&oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), WithLastHint(tt.lastHint))
state.Registers[2] = exec.SysWrite state := goVm.GetState()
state.Registers[4] = exec.FdHintWrite state.GetRegistersRef()[2] = exec.SysWrite
state.Registers[5] = uint32(tt.memOffset) state.GetRegistersRef()[4] = exec.FdHintWrite
state.Registers[6] = uint32(tt.bytesToWrite) state.GetRegistersRef()[5] = uint32(tt.memOffset)
state.GetRegistersRef()[6] = uint32(tt.bytesToWrite)
err := state.Memory.SetMemoryRange(uint32(tt.memOffset), bytes.NewReader(tt.hintData))
require.NoError(t, err) err := state.GetMemory().SetMemoryRange(uint32(tt.memOffset), bytes.NewReader(tt.hintData))
state.Memory.SetMemory(0, insn) require.NoError(t, err)
curStep := state.Step state.GetMemory().SetMemory(0, insn)
curStep := state.GetStep()
us := singlethreaded.NewInstrumentedState(state, &oracle, os.Stdout, os.Stderr, nil) stepWitness, err := goVm.Step(true)
stepWitness, err := us.Step(true) require.NoError(t, err)
require.NoError(t, err) require.Equal(t, tt.expectedHints, oracle.hints)
require.Equal(t, tt.expectedHints, oracle.hints)
evm := testutil.NewMIPSEVM(contracts, addrs) evm := testutil.NewMIPSEVM(v.Contracts)
evm.SetTracer(tracer) evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm) testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn()) evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
goPost, _ := us.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")
}) })
}
} }
} }
func TestEVMFault(t *testing.T) { func TestEVMFault(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
sender := common.Address{0x13, 0x37} sender := common.Address{0x13, 0x37}
env, evmState := testutil.NewEVMEnv(contracts, addrs) versions := GetMipsVersionTestCases(t)
env.Config.Tracer = tracer
cases := []struct { cases := []struct {
name string name string
nextPC uint32 nextPC uint32
...@@ -490,116 +441,132 @@ func TestEVMFault(t *testing.T) { ...@@ -490,116 +441,132 @@ func TestEVMFault(t *testing.T) {
{"jump in delay-slot", 8, 0x0c_00_00_0c}, {"jump in delay-slot", 8, 0x0c_00_00_0c},
} }
for _, tt := range cases { for _, v := range versions {
t.Run(tt.name, func(t *testing.T) { for _, tt := range cases {
state := &singlethreaded.State{Cpu: mipsevm.CpuScalars{PC: 0, NextPC: tt.nextPC}, Memory: memory.NewMemory()} testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
initialState := &singlethreaded.State{Cpu: mipsevm.CpuScalars{PC: 0, NextPC: tt.nextPC}, Memory: state.Memory} t.Run(testName, func(t *testing.T) {
state.Memory.SetMemory(0, tt.insn) env, evmState := testutil.NewEVMEnv(v.Contracts)
env.Config.Tracer = tracer
// set the return address ($ra) to jump into when test completes
state.Registers[31] = testutil.EndAddr goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), WithNextPC(tt.nextPC))
state := goVm.GetState()
us := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil) state.GetMemory().SetMemory(0, tt.insn)
require.Panics(t, func() { _, _ = us.Step(true) }) // set the return address ($ra) to jump into when test completes
state.GetRegistersRef()[31] = testutil.EndAddr
insnProof := initialState.Memory.MerkleProof(0)
encodedWitness, _ := initialState.EncodeWitness() require.Panics(t, func() { _, _ = goVm.Step(true) })
stepWitness := &mipsevm.StepWitness{
State: encodedWitness, insnProof := state.GetMemory().MerkleProof(0)
ProofData: insnProof[:], encodedWitness, _ := state.EncodeWitness()
} stepWitness := &mipsevm.StepWitness{
input := testutil.EncodeStepInput(t, stepWitness, mipsevm.LocalContext{}, contracts.MIPS) State: encodedWitness,
startingGas := uint64(30_000_000) ProofData: insnProof[:],
}
_, _, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, common.U2560) input := testutil.EncodeStepInput(t, stepWitness, mipsevm.LocalContext{}, v.Contracts.Artifacts.MIPS)
require.EqualValues(t, err, vm.ErrExecutionReverted) startingGas := uint64(30_000_000)
logs := evmState.Logs()
require.Equal(t, 0, len(logs)) _, _, err := env.Call(vm.AccountRef(sender), v.Contracts.Addresses.MIPS, input, startingGas, common.U2560)
}) require.EqualValues(t, err, vm.ErrExecutionReverted)
logs := evmState.Logs()
require.Equal(t, 0, len(logs))
})
}
} }
} }
func TestHelloEVM(t *testing.T) { func TestHelloEVM(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
evm := testutil.NewMIPSEVM(contracts, addrs) versions := GetMipsVersionTestCases(t)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
state := testutil.LoadELFProgram(t, "../../testdata/example/bin/hello.elf", singlethreaded.CreateInitialState, true)
var stdOutBuf, stdErrBuf bytes.Buffer
goState := singlethreaded.NewInstrumentedState(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), nil)
start := time.Now()
for i := 0; i < 400_000; i++ {
curStep := goState.GetState().GetStep()
if goState.GetState().GetExited() {
break
}
insn := state.Memory.GetMemory(state.Cpu.PC)
if i%1000 == 0 { // avoid spamming test logs, we are executing many steps
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.Cpu.PC, insn)
}
stepWitness, err := goState.Step(true) for _, v := range versions {
require.NoError(t, err) t.Run(v.Name, func(t *testing.T) {
evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn()) evm := testutil.NewMIPSEVM(v.Contracts)
// verify the post-state matches. evm.SetTracer(tracer)
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison. testutil.LogStepFailureAtCleanup(t, evm)
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
end := time.Now()
delta := end.Sub(start)
t.Logf("test took %s, %d instructions, %s per instruction", delta, state.Step, delta/time.Duration(state.Step))
require.True(t, state.Exited, "must complete program") var stdOutBuf, stdErrBuf bytes.Buffer
require.Equal(t, uint8(0), state.ExitCode, "exit with 0") elfFile := "../../testdata/example/bin/hello.elf"
goVm := v.ElfVMFactory(t, elfFile, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger())
state := goVm.GetState()
require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello") start := time.Now()
require.Equal(t, "", stdErrBuf.String(), "stderr silent") for i := 0; i < 400_000; i++ {
curStep := goVm.GetState().GetStep()
if goVm.GetState().GetExited() {
break
}
insn := state.GetMemory().GetMemory(state.GetPC())
if i%1000 == 0 { // avoid spamming test logs, we are executing many steps
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn)
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
end := time.Now()
delta := end.Sub(start)
t.Logf("test took %s, %d instructions, %s per instruction", delta, state.GetStep(), delta/time.Duration(state.GetStep()))
require.True(t, state.GetExited(), "must complete program")
require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0")
require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello")
require.Equal(t, "", stdErrBuf.String(), "stderr silent")
})
}
} }
func TestClaimEVM(t *testing.T) { func TestClaimEVM(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
evm := testutil.NewMIPSEVM(contracts, addrs) versions := GetMipsVersionTestCases(t)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
state := testutil.LoadELFProgram(t, "../../testdata/example/bin/claim.elf", singlethreaded.CreateInitialState, true) for _, v := range versions {
oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t) t.Run(v.Name, func(t *testing.T) {
evm := testutil.NewMIPSEVM(v.Contracts)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
var stdOutBuf, stdErrBuf bytes.Buffer oracle, expectedStdOut, expectedStdErr := testutil.ClaimTestOracle(t)
goState := singlethreaded.NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), nil)
for i := 0; i < 2000_000; i++ { var stdOutBuf, stdErrBuf bytes.Buffer
curStep := goState.GetState().GetStep() elfFile := "../../testdata/example/bin/claim.elf"
if goState.GetState().GetExited() { goVm := v.ElfVMFactory(t, elfFile, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger())
break state := goVm.GetState()
}
insn := state.Memory.GetMemory(state.Cpu.PC) for i := 0; i < 2000_000; i++ {
if i%1000 == 0 { // avoid spamming test logs, we are executing many steps curStep := goVm.GetState().GetStep()
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.Cpu.PC, insn) if goVm.GetState().GetExited() {
} break
}
insn := state.GetMemory().GetMemory(state.GetPC())
if i%1000 == 0 { // avoid spamming test logs, we are executing many steps
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn)
}
stepWitness, err := goState.Step(true) stepWitness, err := goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, singlethreaded.GetStateHashFn()) evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
goPost, _ := goState.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")
} }
require.True(t, state.Exited, "must complete program") require.True(t, state.GetExited(), "must complete program")
require.Equal(t, uint8(0), state.ExitCode, "exit with 0") require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0")
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")
})
}
} }
type hintTrackingOracle struct { type hintTrackingOracle struct {
......
package tests
import (
"os"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
func TestEVM_CloneFlags(t *testing.T) {
contracts := testutil.TestContractsSetup(t, testutil.MipsMultithreaded)
var tracer *tracing.Hooks
cases := []struct {
name string
flags uint32
valid bool
}{
{"the supported flags bitmask", exec.ValidCloneFlags, true},
{"no flags", 0, false},
{"all flags", ^uint32(0), false},
{"all unsupported flags", ^uint32(exec.ValidCloneFlags), false},
{"a few supported flags", exec.CloneFs | exec.CloneSysvsem, false},
{"one supported flag", exec.CloneFs, false},
{"mixed supported and unsupported flags", exec.CloneFs | exec.CloneParentSettid, false},
{"a single unsupported flag", exec.CloneUntraced, false},
{"multiple unsupported flags", exec.CloneUntraced | exec.CloneParentSettid, false},
}
const insn = uint32(0x00_00_00_0C) // syscall instruction
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
state := multithreaded.CreateEmptyState()
state.Memory.SetMemory(state.GetPC(), insn)
state.GetRegistersRef()[2] = exec.SysClone // Set syscall number
state.GetRegistersRef()[4] = tt.flags // Set first argument
curStep := state.Step
var err error
var stepWitness *mipsevm.StepWitness
us := multithreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
if !tt.valid {
// The VM should exit
stepWitness, err = us.Step(true)
require.NoError(t, err)
require.Equal(t, true, us.GetState().GetExited())
require.Equal(t, uint8(mipsevm.VMStatusPanic), us.GetState().GetExitCode())
} else {
stepWitness, err = us.Step(true)
require.NoError(t, err)
}
evm := testutil.NewMIPSEVM(contracts)
evm.SetTracer(tracer)
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")
})
}
}
package tests
import (
"bytes"
"math/rand"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithPC(pc), WithNextPC(nextPC), WithStep(step), WithPreimageOffset(preimageOffset))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysBrk
state.GetMemory().SetMemory(pc, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = program.PROGRAM_BREAK
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.GetPC())
require.Equal(t, nextPC+4, state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
require.Equal(t, step+1, state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, preimageOffset, state.GetPreimageOffset())
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")
})
}
})
}
func FuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2))
// Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3))
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step), WithHeap(heap))
state := goVm.GetState()
*state.GetRegistersRef() = testutil.RandomRegisters(seed)
state.GetRegistersRef()[2] = exec.SysMmap
state.GetRegistersRef()[4] = addr
state.GetRegistersRef()[5] = siz
state.GetMemory().SetMemory(0, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
preStateRegisters := testutil.CopyRegisters(state)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
var expectedHeap uint32
expectedRegisters := preStateRegisters
if addr == 0 {
sizAlign := siz
if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size
sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask)
}
newHeap := heap + sizAlign
if newHeap > program.HEAP_END || newHeap < heap || sizAlign < siz {
expectedHeap = heap
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedRegisters[2] = heap
expectedRegisters[7] = 0 // no error
expectedHeap = heap + sizAlign
}
} else {
expectedRegisters[2] = addr
expectedRegisters[7] = 0 // no error
expectedHeap = heap
}
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, uint32(0), state.GetPreimageOffset())
require.Equal(t, expectedHeap, state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
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")
})
}
})
}
func FuzzStateSyscallExitGroup(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, exitCode uint8, pc uint32, step uint64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithPC(pc), WithNextPC(nextPC), WithStep(step))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysExitGroup
state.GetRegistersRef()[4] = uint32(exitCode)
state.GetMemory().SetMemory(pc, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
preStateRegisters := testutil.CopyRegisters(state)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc, state.GetCpu().PC)
require.Equal(t, nextPC, state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(exitCode), state.GetExitCode())
require.Equal(t, true, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, preStateRegisters, state.GetRegistersRef())
require.Equal(t, step+1, state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, uint32(0), state.GetPreimageOffset())
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")
})
}
})
}
func FuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd uint32, cmd uint32) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysFcntl
state.GetRegistersRef()[4] = fd
state.GetRegistersRef()[5] = cmd
state.GetMemory().SetMemory(0, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
preStateRegisters := testutil.CopyRegisters(state)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, uint32(0), state.GetPreimageOffset())
if cmd == 3 {
expectedRegisters := preStateRegisters
switch fd {
case exec.FdStdin, exec.FdPreimageRead, exec.FdHintRead:
expectedRegisters[2] = 0
case exec.FdStdout, exec.FdStderr, exec.FdPreimageWrite, exec.FdHintWrite:
expectedRegisters[2] = 1
default:
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = exec.MipsEBADF
}
require.Equal(t, expectedRegisters, state.GetRegistersRef())
} else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = exec.MipsEINVAL
require.Equal(t, expectedRegisters, state.GetRegistersRef())
}
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")
})
}
})
}
func FuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step), WithPreimageKey(preimageKey))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysRead
state.GetRegistersRef()[4] = exec.FdHintRead
state.GetRegistersRef()[5] = addr
state.GetRegistersRef()[6] = count
state.GetMemory().SetMemory(0, syscallInsn)
preStatePreimageKey := state.GetPreimageKey()
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = count
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, preStatePreimageKey, state.GetPreimageKey())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
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")
})
}
})
}
func FuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, preimageOffset uint32) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
preimageData := []byte("hello world")
if preimageOffset >= uint32(len(preimageData)) {
t.SkipNow()
}
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData)
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step), WithPreimageKey(preimageKey), WithPreimageOffset(preimageOffset))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysRead
state.GetRegistersRef()[4] = exec.FdPreimageRead
state.GetRegistersRef()[5] = addr
state.GetRegistersRef()[6] = count
state.GetMemory().SetMemory(0, syscallInsn)
preStatePreimageKey := state.GetPreimageKey()
preStateRoot := state.GetMemory().MerkleRoot()
writeLen := count
if writeLen > 4 {
writeLen = 4
}
if preimageOffset+writeLen > uint32(8+len(preimageData)) {
writeLen = uint32(8+len(preimageData)) - preimageOffset
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.True(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
if writeLen > 0 {
// Memory may be unchanged if we're writing the first zero-valued 7 bytes of the pre-image.
//require.NotEqual(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Greater(t, state.GetPreimageOffset(), preimageOffset)
} else {
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, state.GetPreimageOffset(), preimageOffset)
}
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, preStatePreimageKey, state.GetPreimageKey())
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")
})
}
})
}
func FuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, randSeed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step), WithPreimageKey(preimageKey))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysWrite
state.GetRegistersRef()[4] = exec.FdHintWrite
state.GetRegistersRef()[5] = addr
state.GetRegistersRef()[6] = count
// Set random data at the target memory range
randBytes, err := randomBytes(randSeed, count)
require.NoError(t, err)
err = state.GetMemory().SetMemoryRange(addr, bytes.NewReader(randBytes))
require.NoError(t, err)
// Set syscall instruction
state.GetMemory().SetMemory(0, syscallInsn)
preStatePreimageKey := state.GetPreimageKey()
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = count
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, preStatePreimageKey, state.GetPreimageKey())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
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")
})
}
})
}
func FuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
step := uint64(0)
preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData)
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithStep(step), WithPreimageKey(preimageKey), WithPreimageOffset(128))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysWrite
state.GetRegistersRef()[4] = exec.FdPreimageWrite
state.GetRegistersRef()[5] = addr
state.GetRegistersRef()[6] = count
state.GetMemory().SetMemory(0, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
sz := 4 - (addr & 0x3)
if sz < count {
count = sz
}
expectedRegisters[2] = count
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.GetCpu().PC)
require.Equal(t, uint32(8), state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, uint64(1), state.GetStep())
require.Equal(t, uint32(0), state.GetPreimageOffset())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
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")
})
}
})
}
func randomBytes(seed int64, length uint32) ([]byte, error) {
r := rand.New(rand.NewSource(seed))
randBytes := make([]byte, length)
if _, err := r.Read(randBytes); err != nil {
return nil, err
}
return randBytes, nil
}
package tests
import (
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
// TODO
func FuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f)
// t.Skip is causing linting check to fail, disable for now
//nolint:staticcheck
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
// TODO(cp-903) Customize test for multi-threaded vm
t.Skip("TODO - customize this test for MTCannon")
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithPC(pc), WithNextPC(nextPC), WithStep(step), WithPreimageOffset(preimageOffset))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysClone
state.GetMemory().SetMemory(pc, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = 0x1
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.GetCpu().PC)
require.Equal(t, nextPC+4, state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
require.Equal(t, step+1, state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, preimageOffset, state.GetPreimageOffset())
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")
})
}
package tests
import (
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
func FuzzStateSyscallCloneST(f *testing.F) {
v := GetSingleThreadedTestCase(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
WithPC(pc), WithNextPC(nextPC), WithStep(step), WithPreimageOffset(preimageOffset))
state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysClone
state.GetMemory().SetMemory(pc, syscallInsn)
preStateRoot := state.GetMemory().MerkleRoot()
expectedRegisters := testutil.CopyRegisters(state)
expectedRegisters[2] = 0x1
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.GetCpu().PC)
require.Equal(t, nextPC+4, state.GetCpu().NextPC)
require.Equal(t, uint32(0), state.GetCpu().LO)
require.Equal(t, uint32(0), state.GetCpu().HI)
require.Equal(t, uint32(0), state.GetHeap())
require.Equal(t, uint8(0), state.GetExitCode())
require.Equal(t, false, state.GetExited())
require.Equal(t, preStateRoot, state.GetMemory().MerkleRoot())
require.Equal(t, expectedRegisters, state.GetRegistersRef())
require.Equal(t, step+1, state.GetStep())
require.Equal(t, common.Hash{}, state.GetPreimageKey())
require.Equal(t, preimageOffset, state.GetPreimageOffset())
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")
})
}
package tests
import (
"bytes"
"math/rand"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysBrk},
Step: step,
PreimageKey: common.Hash{},
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = program.PROGRAM_BREAK
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.Cpu.PC)
require.Equal(t, nextPC+4, state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, preimageOffset, state.PreimageOffset)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallClone(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysClone},
Step: step,
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = 0x1
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.Cpu.PC)
require.Equal(t, nextPC+4, state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, preimageOffset, state.PreimageOffset)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallMmap(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
// Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2))
// Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3))
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) {
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: heap,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: testutil.RandomRegisters(seed),
Step: step,
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
state.Registers[2] = exec.SysMmap
state.Registers[4] = addr
state.Registers[5] = siz
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
var expectedHeap uint32
expectedRegisters := preStateRegisters
if addr == 0 {
sizAlign := siz
if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size
sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask)
}
newHeap := heap + sizAlign
if newHeap > program.HEAP_END || newHeap < heap || sizAlign < siz {
expectedHeap = heap
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedRegisters[2] = heap
expectedRegisters[7] = 0 // no error
expectedHeap = heap + sizAlign
}
} else {
expectedRegisters[2] = addr
expectedRegisters[7] = 0 // no error
expectedHeap = heap
}
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallExitGroup(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, exitCode uint8, pc uint32, step uint64) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysExitGroup, 4: uint32(exitCode)},
Step: step,
PreimageOffset: 0,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc, state.Cpu.PC)
require.Equal(t, nextPC, state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(exitCode), state.ExitCode)
require.Equal(t, true, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, preStateRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallFcntl(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
f.Fuzz(func(t *testing.T, fd uint32, cmd uint32) {
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysFcntl, 4: fd, 5: cmd},
Step: step,
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
if cmd == 3 {
expectedRegisters := preStateRegisters
switch fd {
case exec.FdStdin, exec.FdPreimageRead, exec.FdHintRead:
expectedRegisters[2] = 0
case exec.FdStdout, exec.FdStderr, exec.FdPreimageWrite, exec.FdHintWrite:
expectedRegisters[2] = 1
default:
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = exec.MipsEBADF
}
require.Equal(t, expectedRegisters, state.Registers)
} else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = exec.MipsEINVAL
require.Equal(t, expectedRegisters, state.Registers)
}
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateHintRead(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
preimageData := []byte("hello world")
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysRead, 4: exec.FdHintRead, 5: addr, 6: count},
Step: step,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = count
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStatePreimageRead(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, preimageOffset uint32) {
preimageData := []byte("hello world")
if preimageOffset >= uint32(len(preimageData)) {
t.SkipNow()
}
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysRead, 4: exec.FdPreimageRead, 5: addr, 6: count},
Step: step,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
writeLen := count
if writeLen > 4 {
writeLen = 4
}
if preimageOffset+writeLen > uint32(8+len(preimageData)) {
writeLen = uint32(8+len(preimageData)) - preimageOffset
}
oracle := testutil.StaticOracle(t, preimageData)
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.True(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
if writeLen > 0 {
// Memory may be unchanged if we're writing the first zero-valued 7 bytes of the pre-image.
//require.NotEqual(t, preStateRoot, state.Memory.MerkleRoot())
require.Greater(t, state.PreimageOffset, preimageOffset)
} else {
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, state.PreimageOffset, preimageOffset)
}
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateHintWrite(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, randSeed int64) {
preimageData := []byte("hello world")
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysWrite, 4: exec.FdHintWrite, 5: addr, 6: count},
Step: step,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 0,
LastHint: nil,
}
// Set random data at the target memory range
randBytes, err := randomBytes(randSeed, count)
require.NoError(t, err)
err = state.Memory.SetMemoryRange(addr, bytes.NewReader(randBytes))
require.NoError(t, err)
// Set syscall instruction
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = count
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStatePreimageWrite(f *testing.F) {
contracts, addrs := testContractsSetup(f)
step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
preimageData := []byte("hello world")
state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
},
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysWrite, 4: exec.FdPreimageWrite, 5: addr, 6: count},
Step: 0,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 128,
}
state.Memory.SetMemory(0, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
sz := 4 - (addr & 0x3)
if sz < count {
count = sz
}
expectedRegisters[2] = count
oracle := testutil.StaticOracle(t, preimageData)
goState := singlethreaded.NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func randomBytes(seed int64, length uint32) ([]byte, error) {
r := rand.New(rand.NewSource(seed))
randBytes := make([]byte, length)
if _, err := r.Read(randBytes); err != nil {
return nil, err
}
return randBytes, nil
}
package tests
import (
"io"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"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/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
type StateMutator interface {
SetPC(pc uint32)
SetNextPC(nextPC uint32)
SetHeap(addr uint32)
SetLastHint(lastHint hexutil.Bytes)
SetPreimageKey(key common.Hash)
SetPreimageOffset(offset uint32)
SetStep(step uint64)
}
type singlethreadedMutator struct {
state *singlethreaded.State
}
var _ StateMutator = (*singlethreadedMutator)(nil)
func (m *singlethreadedMutator) SetPC(pc uint32) {
m.state.Cpu.PC = pc
}
func (m *singlethreadedMutator) SetNextPC(nextPC uint32) {
m.state.Cpu.NextPC = nextPC
}
func (m *singlethreadedMutator) SetHeap(addr uint32) {
m.state.Heap = addr
}
func (m *singlethreadedMutator) SetLastHint(lastHint hexutil.Bytes) {
m.state.LastHint = lastHint
}
func (m *singlethreadedMutator) SetPreimageKey(key common.Hash) {
m.state.PreimageKey = key
}
func (m *singlethreadedMutator) SetPreimageOffset(offset uint32) {
m.state.PreimageOffset = offset
}
func (m *singlethreadedMutator) SetStep(step uint64) {
m.state.Step = step
}
type multithreadedMutator struct {
state *multithreaded.State
}
var _ StateMutator = (*multithreadedMutator)(nil)
func (m *multithreadedMutator) SetPC(pc uint32) {
thread := m.state.GetCurrentThread()
thread.Cpu.PC = pc
}
func (m *multithreadedMutator) SetHeap(addr uint32) {
m.state.Heap = addr
}
func (m *multithreadedMutator) SetNextPC(nextPC uint32) {
thread := m.state.GetCurrentThread()
thread.Cpu.NextPC = nextPC
}
func (m *multithreadedMutator) SetLastHint(lastHint hexutil.Bytes) {
m.state.LastHint = lastHint
}
func (m *multithreadedMutator) SetPreimageKey(key common.Hash) {
m.state.PreimageKey = key
}
func (m *multithreadedMutator) SetPreimageOffset(offset uint32) {
m.state.PreimageOffset = offset
}
func (m *multithreadedMutator) SetStep(step uint64) {
m.state.Step = step
}
type VMOption func(vm StateMutator)
func WithPC(pc uint32) VMOption {
return func(state StateMutator) {
state.SetPC(pc)
}
}
func WithNextPC(nextPC uint32) VMOption {
return func(state StateMutator) {
state.SetNextPC(nextPC)
}
}
func WithHeap(addr uint32) VMOption {
return func(state StateMutator) {
state.SetHeap(addr)
}
}
func WithLastHint(lastHint hexutil.Bytes) VMOption {
return func(state StateMutator) {
state.SetLastHint(lastHint)
}
}
func WithPreimageKey(key common.Hash) VMOption {
return func(state StateMutator) {
state.SetPreimageKey(key)
}
}
func WithPreimageOffset(offset uint32) VMOption {
return func(state StateMutator) {
state.SetPreimageOffset(offset)
}
}
func WithStep(step uint64) VMOption {
return func(state StateMutator) {
state.SetStep(step)
}
}
type VMFactory func(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...VMOption) mipsevm.FPVM
func singleThreadedVmFactory(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...VMOption) mipsevm.FPVM {
state := singlethreaded.CreateEmptyState()
mutator := &singlethreadedMutator{state: state}
for _, opt := range opts {
opt(mutator)
}
return singlethreaded.NewInstrumentedState(state, po, stdOut, stdErr, nil)
}
func multiThreadedVmFactory(po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, opts ...VMOption) mipsevm.FPVM {
state := multithreaded.CreateEmptyState()
mutator := &multithreadedMutator{state: state}
for _, opt := range opts {
opt(mutator)
}
return multithreaded.NewInstrumentedState(state, po, stdOut, stdErr, log)
}
type ElfVMFactory func(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM
func singleThreadElfVmFactory(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM {
state := testutil.LoadELFProgram(t, elfFile, singlethreaded.CreateInitialState, true)
return singlethreaded.NewInstrumentedState(state, po, stdOut, stdErr, nil)
}
func multiThreadElfVmFactory(t require.TestingT, elfFile string, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM {
state := testutil.LoadELFProgram(t, elfFile, multithreaded.CreateInitialState, false)
return multithreaded.NewInstrumentedState(state, po, stdOut, stdErr, log)
}
type VersionedVMTestCase struct {
Name string
Contracts *testutil.ContractMetadata
StateHashFn mipsevm.HashFn
VMFactory VMFactory
ElfVMFactory ElfVMFactory
}
func GetSingleThreadedTestCase(t require.TestingT) VersionedVMTestCase {
return VersionedVMTestCase{
Name: "single-threaded",
Contracts: testutil.TestContractsSetup(t, testutil.MipsSingleThreaded),
StateHashFn: singlethreaded.GetStateHashFn(),
VMFactory: singleThreadedVmFactory,
ElfVMFactory: singleThreadElfVmFactory,
}
}
func GetMultiThreadedTestCase(t require.TestingT) VersionedVMTestCase {
return VersionedVMTestCase{
Name: "multi-threaded",
Contracts: testutil.TestContractsSetup(t, testutil.MipsMultithreaded),
StateHashFn: multithreaded.GetStateHashFn(),
VMFactory: multiThreadedVmFactory,
ElfVMFactory: multiThreadElfVmFactory,
}
}
func GetMipsVersionTestCases(t require.TestingT) []VersionedVMTestCase {
return []VersionedVMTestCase{
GetSingleThreadedTestCase(t),
GetMultiThreadedTestCase(t),
}
}
...@@ -5,3 +5,10 @@ const BaseAddrEnd = 0xbf_ff_ff_f0 ...@@ -5,3 +5,10 @@ const BaseAddrEnd = 0xbf_ff_ff_f0
// EndAddr is used as return-address for tests // EndAddr is used as return-address for tests
const EndAddr = 0xa7ef00d0 const EndAddr = 0xa7ef00d0
type MipsVersion int
const (
MipsSingleThreaded MipsVersion = iota
MipsMultithreaded
)
...@@ -2,7 +2,6 @@ package testutil ...@@ -2,7 +2,6 @@ package testutil
import ( import (
"debug/elf" "debug/elf"
"testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -10,7 +9,7 @@ import ( ...@@ -10,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
func LoadELFProgram[T mipsevm.FPVMState](t *testing.T, name string, initState program.CreateInitialFPVMState[T], doPatchGo bool) T { func LoadELFProgram[T mipsevm.FPVMState](t require.TestingT, name string, initState program.CreateInitialFPVMState[T], doPatchGo bool) T {
elfProgram, err := elf.Open(name) elfProgram, err := elf.Open(name)
require.NoError(t, err, "open ELF file") require.NoError(t, err, "open ELF file")
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
...@@ -23,9 +24,50 @@ import ( ...@@ -23,9 +24,50 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// LoadArtifacts loads the Cannon contracts, from the contracts package. type Artifacts struct {
func LoadArtifacts() (*Artifacts, error) { MIPS *foundry.Artifact
mips, err := foundry.ReadArtifact("../../../packages/contracts-bedrock/forge-artifacts/MIPS.sol/MIPS.json") Oracle *foundry.Artifact
}
type Addresses struct {
MIPS common.Address
Oracle common.Address
Sender common.Address
FeeRecipient common.Address
}
type ContractMetadata struct {
Artifacts *Artifacts
Addresses *Addresses
}
func TestContractsSetup(t require.TestingT, version MipsVersion) *ContractMetadata {
artifacts, err := loadArtifacts(version)
require.NoError(t, err)
addrs := &Addresses{
MIPS: common.Address{0: 0xff, 19: 1},
Oracle: common.Address{0: 0xff, 19: 2},
Sender: common.Address{0x13, 0x37},
FeeRecipient: common.Address{0xaa},
}
return &ContractMetadata{Artifacts: artifacts, Addresses: addrs}
}
// loadArtifacts loads the Cannon contracts, from the contracts package.
func loadArtifacts(version MipsVersion) (*Artifacts, error) {
var mipsMetadata string
switch version {
case MipsSingleThreaded:
mipsMetadata = "../../../packages/contracts-bedrock/forge-artifacts/MIPS.sol/MIPS.json"
case MipsMultithreaded:
mipsMetadata = "../../../packages/contracts-bedrock/forge-artifacts/MIPS2.sol/MIPS2.json"
default:
return nil, fmt.Errorf("Unknown MipsVersion supplied: %v", version)
}
mips, err := foundry.ReadArtifact(mipsMetadata)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load MIPS contract: %w", err) return nil, fmt.Errorf("failed to load MIPS contract: %w", err)
} }
...@@ -41,19 +83,7 @@ func LoadArtifacts() (*Artifacts, error) { ...@@ -41,19 +83,7 @@ func LoadArtifacts() (*Artifacts, error) {
}, nil }, nil
} }
type Artifacts struct { func NewEVMEnv(contracts *ContractMetadata) (*vm.EVM, *state.StateDB) {
MIPS *foundry.Artifact
Oracle *foundry.Artifact
}
type Addresses struct {
MIPS common.Address
Oracle common.Address
Sender common.Address
FeeRecipient common.Address
}
func NewEVMEnv(artifacts *Artifacts, addrs *Addresses) (*vm.EVM, *state.StateDB) {
// Temporary hack until Cancun is activated on mainnet // Temporary hack until Cancun is activated on mainnet
cpy := *params.MainnetChainConfig cpy := *params.MainnetChainConfig
chainCfg := &cpy // don't modify the global chain config chainCfg := &cpy // don't modify the global chain config
...@@ -74,20 +104,20 @@ func NewEVMEnv(artifacts *Artifacts, addrs *Addresses) (*vm.EVM, *state.StateDB) ...@@ -74,20 +104,20 @@ func NewEVMEnv(artifacts *Artifacts, addrs *Addresses) (*vm.EVM, *state.StateDB)
env := vm.NewEVM(blockContext, vm.TxContext{}, state, chainCfg, vmCfg) env := vm.NewEVM(blockContext, vm.TxContext{}, state, chainCfg, vmCfg)
// pre-deploy the contracts // pre-deploy the contracts
env.StateDB.SetCode(addrs.Oracle, artifacts.Oracle.DeployedBytecode.Object) env.StateDB.SetCode(contracts.Addresses.Oracle, contracts.Artifacts.Oracle.DeployedBytecode.Object)
var mipsCtorArgs [32]byte var mipsCtorArgs [32]byte
copy(mipsCtorArgs[12:], addrs.Oracle[:]) copy(mipsCtorArgs[12:], contracts.Addresses.Oracle[:])
mipsDeploy := append(bytes.Clone(artifacts.MIPS.Bytecode.Object), mipsCtorArgs[:]...) mipsDeploy := append(bytes.Clone(contracts.Artifacts.MIPS.Bytecode.Object), mipsCtorArgs[:]...)
startingGas := uint64(30_000_000) startingGas := uint64(30_000_000)
_, deployedMipsAddr, leftOverGas, err := env.Create(vm.AccountRef(addrs.Sender), mipsDeploy, startingGas, common.U2560) _, deployedMipsAddr, leftOverGas, err := env.Create(vm.AccountRef(contracts.Addresses.Sender), mipsDeploy, startingGas, common.U2560)
if err != nil { if err != nil {
panic(fmt.Errorf("failed to deploy MIPS contract: %w. took %d gas", err, startingGas-leftOverGas)) panic(fmt.Errorf("failed to deploy MIPS contract: %w. took %d gas", err, startingGas-leftOverGas))
} }
addrs.MIPS = deployedMipsAddr contracts.Addresses.MIPS = deployedMipsAddr
rules := env.ChainConfig().Rules(header.Number, true, header.Time) rules := env.ChainConfig().Rules(header.Number, true, header.Time)
env.StateDB.Prepare(rules, addrs.Sender, addrs.FeeRecipient, &addrs.MIPS, vm.ActivePrecompiles(rules), nil) env.StateDB.Prepare(rules, contracts.Addresses.Sender, contracts.Addresses.FeeRecipient, &contracts.Addresses.MIPS, vm.ActivePrecompiles(rules), nil)
return env, state return env, state
} }
......
...@@ -30,9 +30,9 @@ type MIPSEVM struct { ...@@ -30,9 +30,9 @@ type MIPSEVM struct {
lastStepInput []byte lastStepInput []byte
} }
func NewMIPSEVM(artifacts *Artifacts, addrs *Addresses) *MIPSEVM { func NewMIPSEVM(contracts *ContractMetadata) *MIPSEVM {
env, evmState := NewEVMEnv(artifacts, addrs) env, evmState := NewEVMEnv(contracts)
return &MIPSEVM{env, evmState, addrs, nil, artifacts, math.MaxUint64, nil} return &MIPSEVM{env, evmState, contracts.Addresses, nil, contracts.Artifacts, math.MaxUint64, nil}
} }
func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) { func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) {
......
package testutil package testutil
import "math/rand" import (
"math/rand"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
)
func RandomRegisters(seed int64) [32]uint32 { func RandomRegisters(seed int64) [32]uint32 {
r := rand.New(rand.NewSource(seed)) r := rand.New(rand.NewSource(seed))
...@@ -10,3 +14,9 @@ func RandomRegisters(seed int64) [32]uint32 { ...@@ -10,3 +14,9 @@ func RandomRegisters(seed int64) [32]uint32 {
} }
return registers return registers
} }
func CopyRegisters(state mipsevm.FPVMState) *[32]uint32 {
copy := new([32]uint32)
*copy = *state.GetRegistersRef()
return copy
}
...@@ -49,7 +49,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -49,7 +49,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
require.NoError(t, err, "load program into state") require.NoError(t, err, "load program into state")
// set the return address ($ra) to jump into when test completes // set the return address ($ra) to jump into when test completes
state.GetRegisters()[31] = EndAddr state.GetRegistersRef()[31] = EndAddr
us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger()) us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger())
......
...@@ -133,6 +133,21 @@ contract MIPS2_Test is CommonTest { ...@@ -133,6 +133,21 @@ contract MIPS2_Test is CommonTest {
vm.label(address(threading), "Threading"); vm.label(address(threading), "Threading");
} }
/// @notice Used to debug step() behavior given a specific input.
/// This is useful to more easily debug non-forge tests.
/// For example, in cannon/mipsevm/evm_test.go step input can be pulled here:
/// https://github.com/ethereum-optimism/optimism/blob/1f64dd6db5561f3bb76ed1d1ffdaff0cde9b7c4b/cannon/mipsevm/evm_test.go#L80-L80
function test_mips2_step_debug_succeeds() external {
bytes memory input =
hex"e14ced3200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a3df82bcbdf27955e04d467b84d94d0b4662c88a70264d7ea31325bc8d826681ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000affffffff00cbf05eda4a03d05cc6a14cff1cf2f955bfb253097c296ea96032da307da4f353ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb500000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c6000000000000ffffffff000000000000000000000000000000280000002c00000000000000000000000000000000000000010000000000000000000000000000000000000000fffffffd00000003000000000000000000000000000000000000000000000000bffffff00000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7ef00d0ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5ae020008ae11000403e0000800000000000000000000000000000000000000003c10bfff3610fff0341100013c08ffff3508fffd34090003010950212d420001ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d4e545be579dc7118fc02cd7b19b704e4710a81bce0cb48bb7e289e403e7c969a00000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d6a3e23902bafb21ac312e717f7942f8fd8ae795f67c918083442c2ab253cc66e0000000000000000000000000000000000000000000000000000";
(bool success, bytes memory retVal) = address(mips).call(input);
bytes memory expectedRetVal = hex"03fc952a0bd8aabc407669b857af995eab91ce55c404d8b32eaf8b941a48188c";
assertTrue(success);
assertEq(retVal.length, 32, "Expect a bytes32 hash of the post-state to be returned");
assertEq(retVal, expectedRetVal);
}
function test_stepABI_succeeds() public { function test_stepABI_succeeds() public {
uint32[32] memory registers; uint32[32] memory registers;
registers[0] = 0xdeadbeef; registers[0] = 0xdeadbeef;
......
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