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

cannon: Handle preimage bounds checks consistently (#11911)

* cannon: Handle preimage bounds checks consistently

* cannon: Cleanup stray comment
parent 3056348b
...@@ -57,8 +57,8 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) ...@@ -57,8 +57,8 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32)
p.lastPreimage = preimage p.lastPreimage = preimage
} }
p.lastPreimageOffset = offset p.lastPreimageOffset = offset
if offset > uint32(len(preimage)) { if offset >= uint32(len(preimage)) {
return panic("Preimage offset out-of-bounds")
} }
datLen = uint32(copy(dat[:], preimage[offset:])) datLen = uint32(copy(dat[:], preimage[offset:]))
return return
......
...@@ -22,6 +22,7 @@ type ExpectedMTState struct { ...@@ -22,6 +22,7 @@ type ExpectedMTState struct {
Step uint64 Step uint64
LastHint hexutil.Bytes LastHint hexutil.Bytes
MemoryRoot common.Hash MemoryRoot common.Hash
expectedMemory *memory.Memory
// Threading-related expectations // Threading-related expectations
StepsSinceLastContextSwitch uint64 StepsSinceLastContextSwitch uint64
Wakeup uint32 Wakeup uint32
...@@ -34,7 +35,6 @@ type ExpectedMTState struct { ...@@ -34,7 +35,6 @@ type ExpectedMTState struct {
prestateActiveThreadOrig ExpectedThreadState // Cached for internal use prestateActiveThreadOrig ExpectedThreadState // Cached for internal use
ActiveThreadId uint32 ActiveThreadId uint32
threadExpectations map[uint32]*ExpectedThreadState threadExpectations map[uint32]*ExpectedThreadState
memoryExpectations *memory.Memory
} }
type ExpectedThreadState struct { type ExpectedThreadState struct {
...@@ -83,7 +83,7 @@ func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState { ...@@ -83,7 +83,7 @@ func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState {
prestateActiveThreadOrig: *newExpectedThreadState(currentThread), // Cache prestate thread for internal use prestateActiveThreadOrig: *newExpectedThreadState(currentThread), // Cache prestate thread for internal use
ActiveThreadId: currentThread.ThreadId, ActiveThreadId: currentThread.ThreadId,
threadExpectations: expectedThreads, threadExpectations: expectedThreads,
memoryExpectations: fromState.Memory.Copy(), expectedMemory: fromState.Memory.Copy(),
} }
} }
...@@ -113,14 +113,14 @@ func (e *ExpectedMTState) ExpectStep() { ...@@ -113,14 +113,14 @@ func (e *ExpectedMTState) ExpectStep() {
} }
func (e *ExpectedMTState) ExpectMemoryWrite(addr uint32, val uint32) { func (e *ExpectedMTState) ExpectMemoryWrite(addr uint32, val uint32) {
e.memoryExpectations.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.memoryExpectations.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr uint32, val uint32, addr2 uint32, val2 uint32) { func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr uint32, val uint32, addr2 uint32, val2 uint32) {
e.memoryExpectations.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.memoryExpectations.SetMemory(addr2, val) e.expectedMemory.SetMemory(addr2, val)
e.MemoryRoot = e.memoryExpectations.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
func (e *ExpectedMTState) ExpectPreemption(preState *multithreaded.State) { func (e *ExpectedMTState) ExpectPreemption(preState *multithreaded.State) {
......
...@@ -2,6 +2,7 @@ package tests ...@@ -2,6 +2,7 @@ package tests
import ( import (
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"io" "io"
"os" "os"
...@@ -12,12 +13,14 @@ import ( ...@@ -12,12 +13,14 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"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/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
func TestEVM(t *testing.T) { func TestEVM(t *testing.T) {
...@@ -227,6 +230,85 @@ func TestEVM_MMap(t *testing.T) { ...@@ -227,6 +230,85 @@ func TestEVM_MMap(t *testing.T) {
} }
} }
func TestEVM_SysRead_Preimage(t *testing.T) {
var tracer *tracing.Hooks
preimageValue := make([]byte, 0, 8)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32)
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
addr uint32
count uint32
writeLen uint32
preimageOffset uint32
prestateMem uint32
postateMem uint32
shouldPanic bool
}{
{name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_FF_FF_FF},
{name: "Aligned addr, write 2 byte", addr: 0x00_00_FF_00, count: 2, writeLen: 2, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_FF_FF},
{name: "Aligned addr, write 3 byte", addr: 0x00_00_FF_00, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_56_FF},
{name: "Aligned addr, write 4 byte", addr: 0x00_00_FF_00, count: 4, writeLen: 4, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_56_78},
{name: "1-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_01, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_12_FF_FF},
{name: "1-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_01, count: 2, writeLen: 2, preimageOffset: 9, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_34_56_FF},
{name: "1-byte misaligned addr, write 3 byte", addr: 0x00_00_FF_01, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_12_34_56},
{name: "2-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_02, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_12_FF},
{name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 2, writeLen: 2, preimageOffset: 12, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_98_76},
{name: "3-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_03, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_12},
{name: "Count of 0", addr: 0x00_00_FF_03, count: 0, writeLen: 0, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF},
{name: "Count greater than 4", addr: 0x00_00_FF_00, count: 15, writeLen: 4, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_56_78},
{name: "Count greater than 4, unaligned", addr: 0x00_00_FF_01, count: 15, writeLen: 3, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_12_34_56},
{name: "Offset at last byte", addr: 0x00_00_FF_00, count: 4, writeLen: 1, preimageOffset: 15, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x32_FF_FF_FF},
{name: "Offset just out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 16, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true},
{name: "Offset out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 17, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true},
}
for i, c := range cases {
for _, v := range versions {
tName := fmt.Sprintf("%v (%v)", c.name, v.Name)
t.Run(tName, func(t *testing.T) {
effAddr := 0xFFffFFfc & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue)
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPreimageKey(preimageKey), testutil.WithPreimageOffset(c.preimageOffset))
state := goVm.GetState()
step := state.GetStep()
// Set up state
state.GetRegistersRef()[2] = exec.SysRead
state.GetRegistersRef()[4] = exec.FdPreimageRead
state.GetRegistersRef()[5] = c.addr
state.GetRegistersRef()[6] = c.count
state.GetMemory().SetMemory(state.GetPC(), syscallInsn)
state.GetMemory().SetMemory(effAddr, c.prestateMem)
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
expected.Registers[2] = c.writeLen
expected.Registers[7] = 0 // no error
expected.PreimageOffset += c.writeLen
expected.ExpectMemoryWrite(effAddr, c.postateMem)
if c.shouldPanic {
require.Panics(t, func() { _, _ = goVm.Step(true) })
testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, v.Contracts, tracer)
} else {
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer)
}
})
}
}
}
func TestEVMSysWriteHint(t *testing.T) { func TestEVMSysWriteHint(t *testing.T) {
var tracer *tracing.Hooks var tracer *tracing.Hooks
......
...@@ -21,19 +21,24 @@ import ( ...@@ -21,19 +21,24 @@ import (
) )
type MIPSEVM struct { type MIPSEVM struct {
sender vm.AccountRef
startingGas uint64
env *vm.EVM env *vm.EVM
evmState *state.StateDB evmState *state.StateDB
addrs *Addresses addrs *Addresses
localOracle mipsevm.PreimageOracle localOracle mipsevm.PreimageOracle
artifacts *Artifacts artifacts *Artifacts
// Track step execution for logging purposes // Track step execution for logging purposes
lastStep uint64 lastStep uint64
lastStepInput []byte lastStepInput []byte
lastPreimageOracleInput []byte
} }
func NewMIPSEVM(contracts *ContractMetadata) *MIPSEVM { func NewMIPSEVM(contracts *ContractMetadata) *MIPSEVM {
env, evmState := NewEVMEnv(contracts) env, evmState := NewEVMEnv(contracts)
return &MIPSEVM{env, evmState, contracts.Addresses, nil, contracts.Artifacts, math.MaxUint64, nil} sender := vm.AccountRef{0x13, 0x37}
startingGas := uint64(30_000_000)
return &MIPSEVM{sender, startingGas, env, evmState, contracts.Addresses, nil, contracts.Artifacts, math.MaxUint64, nil, nil}
} }
func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) { func (m *MIPSEVM) SetTracer(tracer *tracing.Hooks) {
...@@ -52,23 +57,23 @@ func (m *MIPSEVM) SetSourceMapTracer(t *testing.T, version MipsVersion) { ...@@ -52,23 +57,23 @@ func (m *MIPSEVM) SetSourceMapTracer(t *testing.T, version MipsVersion) {
func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, stateHashFn mipsevm.HashFn) []byte { func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, stateHashFn mipsevm.HashFn) []byte {
m.lastStep = step m.lastStep = step
m.lastStepInput = nil m.lastStepInput = nil
sender := common.Address{0x13, 0x37} m.lastPreimageOracleInput = nil
startingGas := uint64(30_000_000)
// we take a snapshot so we can clean up the state, and isolate the logs of this instruction run. // we take a snapshot so we can clean up the state, and isolate the logs of this instruction run.
snap := m.env.StateDB.Snapshot() snap := m.env.StateDB.Snapshot()
if stepWitness.HasPreimage() { if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset) t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := EncodePreimageOracleInput(t, stepWitness, mipsevm.LocalContext{}, m.localOracle, m.artifacts.Oracle) poInput, err := m.encodePreimageOracleInput(t, stepWitness.PreimageKey, stepWitness.PreimageValue, stepWitness.PreimageOffset, mipsevm.LocalContext{})
m.lastPreimageOracleInput = poInput
require.NoError(t, err, "encode preimage oracle input") require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.Oracle, poInput, startingGas, common.U2560) _, leftOverGas, err := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560)
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas) require.NoErrorf(t, err, "evm should not fail, took %d gas", m.startingGas-leftOverGas)
} }
input := EncodeStepInput(t, stepWitness, mipsevm.LocalContext{}, m.artifacts.MIPS) input := EncodeStepInput(t, stepWitness, mipsevm.LocalContext{}, m.artifacts.MIPS)
m.lastStepInput = input m.lastStepInput = input
ret, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.MIPS, input, startingGas, common.U2560) ret, leftOverGas, err := m.env.Call(m.sender, m.addrs.MIPS, input, m.startingGas, common.U2560)
require.NoError(t, err, "evm should not fail") require.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash") require.Len(t, ret, 32, "expecting 32-byte state hash")
// remember state hash, to check it against state // remember state hash, to check it against state
...@@ -82,7 +87,7 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint ...@@ -82,7 +87,7 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint
require.Equal(t, stateHash, postHash, "logged state must be accurate") require.Equal(t, stateHash, postHash, "logged state must be accurate")
m.env.StateDB.RevertToSnapshot(snap) m.env.StateDB.RevertToSnapshot(snap)
t.Logf("EVM step %d took %d gas, and returned stateHash %s", step, startingGas-leftOverGas, postHash) t.Logf("EVM step %d took %d gas, and returned stateHash %s", step, m.startingGas-leftOverGas, postHash)
return evmPost return evmPost
} }
...@@ -92,46 +97,48 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev ...@@ -92,46 +97,48 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev
return input return input
} }
func EncodePreimageOracleInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsevm.LocalContext, localOracle mipsevm.PreimageOracle, oracle *foundry.Artifact) ([]byte, error) { func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32, localContext mipsevm.LocalContext) ([]byte, error) {
if wit.PreimageKey == ([32]byte{}) { if preimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof") return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof")
} }
localOracle := m.localOracle
oracle := m.artifacts.Oracle
switch preimage.KeyType(wit.PreimageKey[0]) { switch preimage.KeyType(preimageKey[0]) {
case preimage.LocalKeyType: case preimage.LocalKeyType:
if len(wit.PreimageValue) > 32+8 { if len(preimageValue) > 32+8 {
return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", wit.PreimageKey) return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", preimageKey)
} }
preimagePart := wit.PreimageValue[8:] preimagePart := preimageValue[8:]
var tmp [32]byte var tmp [32]byte
copy(tmp[:], preimagePart) copy(tmp[:], preimagePart)
input, err := oracle.ABI.Pack("loadLocalData", input, err := oracle.ABI.Pack("loadLocalData",
new(big.Int).SetBytes(wit.PreimageKey[1:]), new(big.Int).SetBytes(preimageKey[1:]),
localContext, localContext,
tmp, tmp,
new(big.Int).SetUint64(uint64(len(preimagePart))), new(big.Int).SetUint64(uint64(len(preimagePart))),
new(big.Int).SetUint64(uint64(wit.PreimageOffset)), new(big.Int).SetUint64(uint64(preimageOffset)),
) )
require.NoError(t, err) require.NoError(t, err)
return input, nil return input, nil
case preimage.Keccak256KeyType: case preimage.Keccak256KeyType:
input, err := oracle.ABI.Pack( input, err := oracle.ABI.Pack(
"loadKeccak256PreimagePart", "loadKeccak256PreimagePart",
new(big.Int).SetUint64(uint64(wit.PreimageOffset)), new(big.Int).SetUint64(uint64(preimageOffset)),
wit.PreimageValue[8:]) preimageValue[8:])
require.NoError(t, err) require.NoError(t, err)
return input, nil return input, nil
case preimage.PrecompileKeyType: case preimage.PrecompileKeyType:
if localOracle == nil { if localOracle == nil {
return nil, errors.New("local oracle is required for precompile preimages") return nil, errors.New("local oracle is required for precompile preimages")
} }
preimage := localOracle.GetPreimage(preimage.Keccak256Key(wit.PreimageKey).PreimageKey()) preimage := localOracle.GetPreimage(preimage.Keccak256Key(preimageKey).PreimageKey())
precompile := common.BytesToAddress(preimage[:20]) precompile := common.BytesToAddress(preimage[:20])
requiredGas := binary.BigEndian.Uint64(preimage[20:28]) requiredGas := binary.BigEndian.Uint64(preimage[20:28])
callInput := preimage[28:] callInput := preimage[28:]
input, err := oracle.ABI.Pack( input, err := oracle.ABI.Pack(
"loadPrecompilePreimagePart", "loadPrecompilePreimagePart",
new(big.Int).SetUint64(uint64(wit.PreimageOffset)), new(big.Int).SetUint64(uint64(preimageOffset)),
precompile, precompile,
requiredGas, requiredGas,
callInput, callInput,
...@@ -140,15 +147,23 @@ func EncodePreimageOracleInput(t *testing.T, wit *mipsevm.StepWitness, localCont ...@@ -140,15 +147,23 @@ func EncodePreimageOracleInput(t *testing.T, wit *mipsevm.StepWitness, localCont
return input, nil return input, nil
default: default:
return nil, fmt.Errorf("unsupported pre-image type %d, cannot prepare preimage with key %x offset %d for oracle", return nil, fmt.Errorf("unsupported pre-image type %d, cannot prepare preimage with key %x offset %d for oracle",
wit.PreimageKey[0], wit.PreimageKey, wit.PreimageOffset) preimageKey[0], preimageKey, preimageOffset)
} }
} }
func (m *MIPSEVM) assertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32) {
poInput, err := m.encodePreimageOracleInput(t, preimageKey, preimageValue, preimageOffset, mipsevm.LocalContext{})
require.NoError(t, err, "encode preimage oracle input")
_, _, evmErr := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560)
require.ErrorContains(t, evmErr, "execution reverted")
}
func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) { func LogStepFailureAtCleanup(t *testing.T, mipsEvm *MIPSEVM) {
t.Cleanup(func() { t.Cleanup(func() {
if t.Failed() { if t.Failed() {
// Note: For easier debugging of a failing step, see MIPS.t.sol#test_step_debug_succeeds() // Note: For easier debugging of a failing step, see MIPS.t.sol#test_step_debug_succeeds()
t.Logf("Failed while executing step %d with input: %x", mipsEvm.lastStep, mipsEvm.lastStepInput) t.Logf("Failed while executing step %d with\n\tstep input: %x\n\tpreimageOracle input: %x", mipsEvm.lastStep, mipsEvm.lastStepInput, mipsEvm.lastPreimageOracleInput)
} }
}) })
} }
...@@ -184,3 +199,11 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract ...@@ -184,3 +199,11 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract
logs := evmState.Logs() logs := evmState.Logs()
require.Equal(t, 0, len(logs)) require.Equal(t, 0, len(logs))
} }
func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32, contracts *ContractMetadata, tracer *tracing.Hooks) {
evm := NewMIPSEVM(contracts)
evm.SetTracer(tracer)
LogStepFailureAtCleanup(t, evm)
evm.assertPreimageOracleReverts(t, preimageKey, preimageValue, preimageOffset)
}
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
func CopyRegisters(state mipsevm.FPVMState) *[32]uint32 { func CopyRegisters(state mipsevm.FPVMState) *[32]uint32 {
...@@ -115,6 +116,7 @@ type ExpectedState struct { ...@@ -115,6 +116,7 @@ type ExpectedState struct {
LastHint hexutil.Bytes LastHint hexutil.Bytes
Registers [32]uint32 Registers [32]uint32
MemoryRoot common.Hash MemoryRoot common.Hash
expectedMemory *memory.Memory
} }
func NewExpectedState(fromState mipsevm.FPVMState) *ExpectedState { func NewExpectedState(fromState mipsevm.FPVMState) *ExpectedState {
...@@ -132,9 +134,22 @@ func NewExpectedState(fromState mipsevm.FPVMState) *ExpectedState { ...@@ -132,9 +134,22 @@ func NewExpectedState(fromState mipsevm.FPVMState) *ExpectedState {
LastHint: fromState.GetLastHint(), LastHint: fromState.GetLastHint(),
Registers: *fromState.GetRegistersRef(), Registers: *fromState.GetRegistersRef(),
MemoryRoot: fromState.GetMemory().MerkleRoot(), MemoryRoot: fromState.GetMemory().MerkleRoot(),
expectedMemory: fromState.GetMemory().Copy(),
} }
} }
func (e *ExpectedState) ExpectStep() {
// Set some standard expectations for a normal step
e.Step += 1
e.PC += 4
e.NextPC += 4
}
func (e *ExpectedState) ExpectMemoryWrite(addr uint32, val uint32) {
e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot()
}
type StateValidationFlags int type StateValidationFlags int
// TODO(cp-983) - Remove these validation hacks // TODO(cp-983) - Remove these validation hacks
......
...@@ -27,10 +27,15 @@ contract MIPS_Test is CommonTest { ...@@ -27,10 +27,15 @@ contract MIPS_Test is CommonTest {
/// For example, in cannon/mipsevm/evm_test.go step input can be pulled here: /// 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 /// https://github.com/ethereum-optimism/optimism/blob/1f64dd6db5561f3bb76ed1d1ffdaff0cde9b7c4b/cannon/mipsevm/evm_test.go#L80-L80
function test_step_debug_succeeds() external { function test_step_debug_succeeds() external {
bytes memory oracleInput =
hex"e15926110000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000081234567898765432000000000000000000000000000000000000000000000000";
(bool oracleSuccess,) = address(oracle).call(oracleInput);
assertTrue(oracleSuccess);
bytes memory input = bytes memory input =
hex"e14ced3200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e29d9267b33909f55f0cac54cca6427fc17fe0b9efe6c34350ca86605145633fe60000000000000000000000000000000000000000000000000000000000000000000000000008a82c0008a8300000000000000000050000000000000000000000000a000000000008a82c0000000000000000000000007fffd00400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007fffd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007008fbc00601000ffbd00000000afbffffc27bdfffcafbf000027bdfff4afa400048fb000408fb100448fb200488fb3004c8fb400508fb600548fb800588fb9005caf0ea4ff7928d10f76bc70a73c867e2c9232b35112b060285aedb7b33533e5253d8c8392c902f20c8314d10a807c7bc5de8126736a03c2bdb47667bbfa710375ac2064c969079ba2acbb688f31cd2bdd4cbe18111a8e733c487884d541f1366231e3c23a71546c755f18517db871301ffe7b08676258520720f4cb2fcc23642ddb778fcfd0cfc38a190d5b8bf0bea4e1ce7d2f35d6a068b94cf6d925e0a024d571f505296c77e19f2ea496d88d90cd342189bdedb389ac959bb9b82db9e324d150eb233dbfa0746469950cec5d4f6360fa49f835403946268bf6fa7472bc4a1320e6a7387a6272dcbf4f8280ff680ccedd4b79f02c78eaef3218c1e78f9e266b3b1c7c1c24e7d04470d63c4e1df858f2e7bf9c7cdad07ed51bc383f80a4d3ded70f2c60642ed3c85483b60ea68b5ed606b2f84d90ac876c01c4d01ba58c431390bd20a29a10d408854c7f62a262860ebd3e4af402fa2dcc0662dfa36fd3e85cbab4da3e3d0907392666337a62e5a663c04bc0a3e16d559f825ba2d40a1c81f4d3844b6cfb2e084a80eb9338fad62b25cf83f4738b5c4df1a0ef019e8ebb9d03d8c7e091f91c853d19da08da2245b01b806c73dc0b200672d04e78982eaed71ea20929c6c472715d5c74a31176a5663348a3c02c89ff5edb7cf3041110e8fe44e2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef6834d8ef8faaf96b7b45235297538a266eb882b8b5680f621aab3417d43cdc2eb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968a60155a81a637d8581c4d275380f7dd05bd2dd27ac3fb7ca905e7aec4e0a1cd99867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619eff0eb509051ae684fe17ee4373a3c82e88f970dab89cc0915d21b63cb96415cc2b8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0"; hex"e14ced3200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2d0637506e48299469d82d71a947c6771e7d3eaa3db30b44b30b3ac6ba6596c79029df7322a2404a59aebdffb81ab72dd22cc4459131675e63231b2c693676cf100000008f1f85ff4f1f85ff82ad7bda1e5acea1049e0bfd000001f5b0412ffd341c045e665389becadf100000fa3c64fbd7f000000050000ff00000000015b3d97166d1aec28829f3dd43d8cf1f9358e4103b16d09d466e2c7c048ea3ba1aef3141e700270581aa0b75b50e34fc926bb2d83bb3938f8506d442d5e545ba3a5d214515c11955d8ad50cfb04a6a0e484a2a29f1d688138c1883f289a45a6d5d9c37ebe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618df7a66599e9dd7409a7d8de62e29bea7821f0f19cfb783952bff507c87eba2365ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d53cee4e252442dad999b85c644aa0a6fd8bca90f35f1a5ae696cb8d9eb5a35be";
(bool success, bytes memory retVal) = address(mips).call(input); (bool success, bytes memory retVal) = address(mips).call(input);
bytes memory expectedRetVal = hex"03611b9f88f952fea10b9330e85fbe7b8ddd0d457d3f31e647434a5330789212"; bytes memory expectedRetVal = hex"03dacdac4e61d89774a305dd0828063706ad878bb6353c0c2cd787d1e5cddd67";
assertTrue(success); assertTrue(success);
assertEq(retVal.length, 32, "Expect a bytes32 hash of the post-state to be returned"); assertEq(retVal.length, 32, "Expect a bytes32 hash of the post-state to be returned");
......
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