Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannon: Run common evm tests across all implementations #11333

Merged
Merged
6 changes: 5 additions & 1 deletion cannon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ test: elf contract
go test -v ./...

fuzz:
# Common vm tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm/tests
mbaxter marked this conversation as resolved.
Show resolved Hide resolved
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=FuzzStateSyscallExitGroup ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./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 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
mbaxter marked this conversation as resolved.
Show resolved Hide resolved

.PHONY: \
cannon \
Expand Down
27 changes: 25 additions & 2 deletions cannon/mipsevm/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@ package mipsevm

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"

"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
)

type FPVMState interface {
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() uint32

// GetRegisters returns the currently active registers
GetRegisters() *[32]uint32
// GetCpu returns the currently active cpu scalars, including the program counter
GetCpu() CpuScalars

// GetRegistersRef returns a pointer to the currently active registers
GetRegistersRef() *[32]uint32

// GetStep returns the current VM step
GetStep() uint64
Expand All @@ -24,6 +37,16 @@ type FPVMState interface {
// GetExitCode returns the exit code
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() (witness []byte, hash common.Hash)
}
Expand Down
8 changes: 4 additions & 4 deletions cannon/mipsevm/multithreaded/mips.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
)

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)
v1 := uint32(0)

Expand Down Expand Up @@ -188,7 +188,7 @@ func (m *InstrumentedState) mipsStep() error {
return nil
}
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.
// Don't allow regular execution until we have found such a thread or else we have visited all threads.
Expand Down Expand Up @@ -264,7 +264,7 @@ func (m *InstrumentedState) mipsStep() error {
}

// 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) {
Expand Down
2 changes: 1 addition & 1 deletion cannon/mipsevm/multithreaded/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (t *ThreadedStackTrackerImpl) Traceback() {
}

func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracker {
thread := t.state.getCurrentThread()
thread := t.state.GetCurrentThread()
tracker, exists := t.trackersByThreadId[thread.ThreadId]
if !exists {
tracker = exec.NewStackTrackerUnsafe(t.state, t.meta)
Expand Down
50 changes: 35 additions & 15 deletions cannon/mipsevm/multithreaded/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,6 @@ type State struct {
NextThreadId uint32 `json:"nextThreadId"`

// 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"`
}

Expand All @@ -83,15 +76,15 @@ func CreateEmptyState() *State {

func CreateInitialState(pc, heapStart uint32) *State {
state := CreateEmptyState()
currentThread := state.getCurrentThread()
currentThread := state.GetCurrentThread()
currentThread.Cpu.PC = pc
currentThread.Cpu.NextPC = pc + 4
state.Heap = heapStart

return state
}

func (s *State) getCurrentThread() *ThreadState {
func (s *State) GetCurrentThread() *ThreadState {
activeStack := s.getActiveThreadStack()

activeStackSize := len(activeStack)
Expand Down Expand Up @@ -131,17 +124,22 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash {
}

func (s *State) GetPC() uint32 {
activeThread := s.getCurrentThread()
activeThread := s.GetCurrentThread()
return activeThread.Cpu.PC
}

func (s *State) GetRegisters() *[32]uint32 {
activeThread := s.getCurrentThread()
return &activeThread.Registers
func (s *State) GetCpu() mipsevm.CpuScalars {
activeThread := s.GetCurrentThread()
return activeThread.Cpu
}

func (s *State) getCpuRef() *mipsevm.CpuScalars {
return &s.GetCurrentThread().Cpu
}

func (s *State) getCpu() *mipsevm.CpuScalars {
return &s.getCurrentThread().Cpu
func (s *State) GetRegistersRef() *[32]uint32 {
activeThread := s.GetCurrentThread()
return &activeThread.Registers
}

func (s *State) GetExitCode() uint8 { return s.ExitCode }
Expand All @@ -150,6 +148,10 @@ func (s *State) GetExited() bool { return s.Exited }

func (s *State) GetStep() uint64 { return s.Step }

func (s *State) GetLastHint() hexutil.Bytes {
return s.LastHint
}

func (s *State) VMStatus() uint8 {
return mipsevm.VmStatus(s.Exited, s.ExitCode)
}
Expand All @@ -158,6 +160,18 @@ func (s *State) GetMemory() *memory.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) {
out := make([]byte, 0, STATE_WITNESS_SIZE)
memRoot := s.Memory.MerkleRoot()
Expand Down Expand Up @@ -214,6 +228,12 @@ func (sw StateWitness) StateHash() (common.Hash, error) {
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 {
if len(sw) != STATE_WITNESS_SIZE {
panic("Invalid witness length")
Expand Down
4 changes: 2 additions & 2 deletions cannon/mipsevm/multithreaded/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestState_EmptyThreadsRoot(t *testing.T) {
func TestState_EncodeThreadProof_SingleThread(t *testing.T) {
state := CreateEmptyState()
// Set some fields on the active thread
activeThread := state.getCurrentThread()
activeThread := state.GetCurrentThread()
activeThread.Cpu.PC = 4
activeThread.Cpu.NextPC = 8
activeThread.Cpu.HI = 11
Expand Down Expand Up @@ -181,7 +181,7 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
expectedRoot = crypto.Keccak256Hash(hashData)
}

expectedProof := append([]byte{}, state.getCurrentThread().serializeThread()[:]...)
expectedProof := append([]byte{}, state.GetCurrentThread().serializeThread()[:]...)
expectedProof = append(expectedProof, expectedRoot[:]...)

actualProof := state.EncodeThreadProof()
Expand Down
2 changes: 1 addition & 1 deletion cannon/mipsevm/program/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
return fmt.Errorf("failed to allocate page for stack content")
}
st.GetRegisters()[29] = sp
st.GetRegistersRef()[29] = sp

storeMem := func(addr uint32, v uint32) {
var dat [4]byte
Expand Down
27 changes: 19 additions & 8 deletions cannon/mipsevm/singlethreaded/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ type State struct {
Registers [32]uint32 `json:"registers"`

// 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"`
}

Expand Down Expand Up @@ -130,14 +123,20 @@ func (s *State) UnmarshalJSON(data []byte) error {

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) GetExited() bool { return s.Exited }

func (s *State) GetStep() uint64 { return s.Step }

func (s *State) GetLastHint() hexutil.Bytes {
return s.LastHint
}

func (s *State) VMStatus() uint8 {
return mipsevm.VmStatus(s.Exited, s.ExitCode)
}
Expand All @@ -146,6 +145,18 @@ func (s *State) GetMemory() *memory.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) {
out := make([]byte, 0, STATE_WITNESS_SIZE)
memRoot := s.Memory.MerkleRoot()
Expand Down
Loading