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()
......
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")
})
}
}
This diff is collapsed.
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")
})
}
This diff is collapsed.
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