Commit a2653a38 authored by Inphi's avatar Inphi Committed by GitHub

cannon: 64-bit Refactor (#12029)

* cannon: 64-bit Refactor

Refactor Cannon codebase to support both 32-bit and 64-bit MIPS emulation
while reusing code as much as possible.

* fix 64-bit test compilation errors

* review comments

* more review comments

* fcntl syscall err for 64-bit

* simplify pad check

* cannon: sc must store lsb 32 on 64-bits

* lint

* fix sc 64-bit logic

* add TODO state test
parent fed6f354
...@@ -162,6 +162,9 @@ jobs: ...@@ -162,6 +162,9 @@ jobs:
description: Whether to notify on failure description: Whether to notify on failure
type: boolean type: boolean
default: false default: false
mips64:
type: boolean
default: false
resource_class: xlarge resource_class: xlarge
steps: steps:
- checkout - checkout
...@@ -184,14 +187,29 @@ jobs: ...@@ -184,14 +187,29 @@ jobs:
command: | command: |
make lint make lint
working_directory: cannon working_directory: cannon
- when:
condition:
not: <<parameters.mips64>>
steps:
- run: - run:
name: Cannon Go tests name: Cannon Go 32-bit tests
command: | command: |
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>> export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
mkdir -p /testlogs mkdir -p /testlogs
gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \ gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \
-- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./... -- -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./...
working_directory: cannon working_directory: cannon
- when:
condition: <<parameters.mips64>>
steps:
- run:
name: Cannon Go 64-bit tests
command: |
export SKIP_SLOW_TESTS=<<parameters.skip_slow_tests>>
mkdir -p /testlogs
gotestsum --format=testname --junitfile=/tmp/test-results/cannon.xml --jsonfile=/testlogs/log.json \
-- --tags=cannon64 -parallel=8 -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage.out ./...
working_directory: cannon
- run: - run:
name: upload Cannon coverage name: upload Cannon coverage
command: codecov --verbose --clean --flags cannon-go-tests command: codecov --verbose --clean --flags cannon-go-tests
......
...@@ -15,18 +15,25 @@ endif ...@@ -15,18 +15,25 @@ endif
.DEFAULT_GOAL := cannon .DEFAULT_GOAL := cannon
cannon-impl: cannon32-impl:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon-impl . env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/cannon32-impl .
cannon-embeds: cannon-impl cannon64-impl:
@cp bin/cannon-impl ./multicannon/embeds/cannon-2 env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon64 -v $(LDFLAGS) -o ./bin/cannon64-impl .
@cp bin/cannon-impl ./multicannon/embeds/cannon-1
cannon-embeds: cannon32-impl cannon64-impl
# singlethreaded-v2
@cp bin/cannon32-impl ./multicannon/embeds/cannon-2
# multithreaded
@cp bin/cannon32-impl ./multicannon/embeds/cannon-1
# 64-bit multithreaded
@cp bin/cannon64-impl ./multicannon/embeds/cannon-3
cannon: cannon-embeds cannon: cannon-embeds
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/ env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/
clean: clean:
rm -rf bin rm -rf bin multicannon/embeds/cannon*
elf: elf:
make -C ./testdata/example elf make -C ./testdata/example elf
...@@ -84,9 +91,10 @@ fuzz: ...@@ -84,9 +91,10 @@ fuzz:
go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests
.PHONY: \ .PHONY: \
cannon \ cannon32-impl \
cannon-impl \ cannon64-impl \
cannon-embeds \ cannon-embeds \
cannon \
clean \ clean \
test \ test \
lint \ lint \
......
...@@ -25,7 +25,7 @@ var ( ...@@ -25,7 +25,7 @@ var (
} }
LoadELFPathFlag = &cli.PathFlag{ LoadELFPathFlag = &cli.PathFlag{
Name: "path", Name: "path",
Usage: "Path to 32-bit big-endian MIPS ELF file", Usage: "Path to 32/64-bit big-endian MIPS ELF file",
TakesFile: true, TakesFile: true,
Required: true, Required: true,
} }
...@@ -80,7 +80,7 @@ func LoadELF(ctx *cli.Context) error { ...@@ -80,7 +80,7 @@ func LoadELF(ctx *cli.Context) error {
} }
return program.PatchStack(state) return program.PatchStack(state)
} }
case versions.VersionMultiThreaded: case versions.VersionMultiThreaded, versions.VersionMultiThreaded64:
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
return program.LoadELF(f, multithreaded.CreateInitialState) return program.LoadELF(f, multithreaded.CreateInitialState)
} }
......
...@@ -11,19 +11,19 @@ import ( ...@@ -11,19 +11,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/pkg/profile" "github.com/pkg/profile"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
) )
var ( var (
...@@ -128,7 +128,7 @@ type Proof struct { ...@@ -128,7 +128,7 @@ type Proof struct {
OracleKey hexutil.Bytes `json:"oracle-key,omitempty"` OracleKey hexutil.Bytes `json:"oracle-key,omitempty"`
OracleValue hexutil.Bytes `json:"oracle-value,omitempty"` OracleValue hexutil.Bytes `json:"oracle-value,omitempty"`
OracleOffset uint32 `json:"oracle-offset,omitempty"` OracleOffset arch.Word `json:"oracle-offset,omitempty"`
} }
type rawHint string type rawHint string
...@@ -288,7 +288,7 @@ func Run(ctx *cli.Context) error { ...@@ -288,7 +288,7 @@ func Run(ctx *cli.Context) error {
stopAtAnyPreimage := false stopAtAnyPreimage := false
var stopAtPreimageKeyPrefix []byte var stopAtPreimageKeyPrefix []byte
stopAtPreimageOffset := uint32(0) stopAtPreimageOffset := arch.Word(0)
if ctx.IsSet(RunStopAtPreimageFlag.Name) { if ctx.IsSet(RunStopAtPreimageFlag.Name) {
val := ctx.String(RunStopAtPreimageFlag.Name) val := ctx.String(RunStopAtPreimageFlag.Name)
parts := strings.Split(val, "@") parts := strings.Split(val, "@")
...@@ -297,11 +297,11 @@ func Run(ctx *cli.Context) error { ...@@ -297,11 +297,11 @@ func Run(ctx *cli.Context) error {
} }
stopAtPreimageKeyPrefix = common.FromHex(parts[0]) stopAtPreimageKeyPrefix = common.FromHex(parts[0])
if len(parts) == 2 { if len(parts) == 2 {
x, err := strconv.ParseUint(parts[1], 10, 32) x, err := strconv.ParseUint(parts[1], 10, arch.WordSizeBytes)
if err != nil { if err != nil {
return fmt.Errorf("invalid preimage offset: %w", err) return fmt.Errorf("invalid preimage offset: %w", err)
} }
stopAtPreimageOffset = uint32(x) stopAtPreimageOffset = arch.Word(x)
} }
} else { } else {
switch ctx.String(RunStopAtPreimageTypeFlag.Name) { switch ctx.String(RunStopAtPreimageTypeFlag.Name) {
...@@ -463,7 +463,7 @@ func Run(ctx *cli.Context) error { ...@@ -463,7 +463,7 @@ func Run(ctx *cli.Context) error {
} }
lastPreimageKey, lastPreimageValue, lastPreimageOffset := vm.LastPreimage() lastPreimageKey, lastPreimageValue, lastPreimageOffset := vm.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^arch.Word(0) {
if stopAtAnyPreimage { if stopAtAnyPreimage {
l.Info("Stopping at preimage read") l.Info("Stopping at preimage read")
break break
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "cannon" app.Name = os.Args[0]
app.Usage = "MIPS Fault Proof tool" app.Usage = "MIPS Fault Proof tool"
app.Description = "MIPS Fault Proof tool" app.Description = "MIPS Fault Proof tool"
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
......
//go:build !cannon64
// +build !cannon64
package arch
import "encoding/binary"
type (
// Word differs from the tradditional meaning in MIPS. The type represents the *maximum* architecture specific access length and value sizes.
Word = uint32
// SignedInteger specifies the maximum signed integer type used for arithmetic.
SignedInteger = int32
)
const (
IsMips32 = true
WordSize = 32
WordSizeBytes = WordSize >> 3
PageAddrSize = 12
PageKeySize = WordSize - PageAddrSize
MemProofLeafCount = 28
MemProofSize = MemProofLeafCount * 32
AddressMask = 0xFFffFFfc
ExtMask = 0x3
HeapStart = 0x05_00_00_00
HeapEnd = 0x60_00_00_00
ProgramBreak = 0x40_00_00_00
HighMemoryStart = 0x7f_ff_d0_00
)
var ByteOrderWord = byteOrder32{}
type byteOrder32 struct{}
func (bo byteOrder32) Word(b []byte) Word {
return binary.BigEndian.Uint32(b)
}
func (bo byteOrder32) AppendWord(b []byte, v uint32) []byte {
return binary.BigEndian.AppendUint32(b, v)
}
func (bo byteOrder32) PutWord(b []byte, v uint32) {
binary.BigEndian.PutUint32(b, v)
}
//go:build cannon64
// +build cannon64
package arch
import "encoding/binary"
type (
// Word differs from the tradditional meaning in MIPS. The type represents the *maximum* architecture specific access length and value sizes
Word = uint64
// SignedInteger specifies the maximum signed integer type used for arithmetic.
SignedInteger = int64
)
const (
IsMips32 = false
WordSize = 64
WordSizeBytes = WordSize >> 3
PageAddrSize = 12
PageKeySize = WordSize - PageAddrSize
MemProofLeafCount = 60
MemProofSize = MemProofLeafCount * 32
AddressMask = 0xFFFFFFFFFFFFFFF8
ExtMask = 0x7
HeapStart = 0x10_00_00_00_00_00_00_00
HeapEnd = 0x60_00_00_00_00_00_00_00
ProgramBreak = 0x40_00_00_00_00_00_00_00
HighMemoryStart = 0x7F_FF_FF_FF_D0_00_00_00
)
var ByteOrderWord = byteOrder64{}
type byteOrder64 struct{}
func (bo byteOrder64) Word(b []byte) Word {
return binary.BigEndian.Uint64(b)
}
func (bo byteOrder64) AppendWord(b []byte, v uint64) []byte {
return binary.BigEndian.AppendUint64(b, v)
}
func (bo byteOrder64) PutWord(b []byte, v uint64) {
binary.BigEndian.PutUint64(b, v)
}
package arch
type ByteOrder interface {
Word([]byte) Word
AppendWord([]byte, Word) []byte
PutWord([]byte, Word)
}
...@@ -7,12 +7,12 @@ import ( ...@@ -7,12 +7,12 @@ import (
) )
type MemTracker interface { type MemTracker interface {
TrackMemAccess(addr uint32) TrackMemAccess(addr Word)
} }
type MemoryTrackerImpl struct { type MemoryTrackerImpl struct {
memory *memory.Memory memory *memory.Memory
lastMemAccess uint32 lastMemAccess Word
memProofEnabled bool memProofEnabled bool
// proof of first unique memory access // proof of first unique memory access
memProof [memory.MEM_PROOF_SIZE]byte memProof [memory.MEM_PROOF_SIZE]byte
...@@ -24,9 +24,9 @@ func NewMemoryTracker(memory *memory.Memory) *MemoryTrackerImpl { ...@@ -24,9 +24,9 @@ func NewMemoryTracker(memory *memory.Memory) *MemoryTrackerImpl {
return &MemoryTrackerImpl{memory: memory} return &MemoryTrackerImpl{memory: memory}
} }
func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { func (m *MemoryTrackerImpl) TrackMemAccess(effAddr Word) {
if m.memProofEnabled && m.lastMemAccess != effAddr { if m.memProofEnabled && m.lastMemAccess != effAddr {
if m.lastMemAccess != ^uint32(0) { if m.lastMemAccess != ^Word(0) {
panic(fmt.Errorf("unexpected different mem access at %08x, already have access at %08x buffered", effAddr, m.lastMemAccess)) panic(fmt.Errorf("unexpected different mem access at %08x, already have access at %08x buffered", effAddr, m.lastMemAccess))
} }
m.lastMemAccess = effAddr m.lastMemAccess = effAddr
...@@ -36,7 +36,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) { ...@@ -36,7 +36,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr uint32) {
// TrackMemAccess2 creates a proof for a memory access following a call to TrackMemAccess // TrackMemAccess2 creates a proof for a memory access following a call to TrackMemAccess
// This is used to generate proofs for contiguous memory accesses within the same step // This is used to generate proofs for contiguous memory accesses within the same step
func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) { func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr Word) {
if m.memProofEnabled && m.lastMemAccess+4 != effAddr { if m.memProofEnabled && m.lastMemAccess+4 != effAddr {
panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess)) panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess))
} }
...@@ -46,7 +46,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) { ...@@ -46,7 +46,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr uint32) {
func (m *MemoryTrackerImpl) Reset(enableProof bool) { func (m *MemoryTrackerImpl) Reset(enableProof bool) {
m.memProofEnabled = enableProof m.memProofEnabled = enableProof
m.lastMemAccess = ^uint32(0) m.lastMemAccess = ^Word(0)
} }
func (m *MemoryTrackerImpl) MemProof() [memory.MEM_PROOF_SIZE]byte { func (m *MemoryTrackerImpl) MemProof() [memory.MEM_PROOF_SIZE]byte {
......
This diff is collapsed.
...@@ -8,10 +8,18 @@ import ( ...@@ -8,10 +8,18 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"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"
) )
type Word = arch.Word
const (
AddressMask = arch.AddressMask
)
// TODO(#12205): redefine syscalls for MIPS64
// Syscall codes // Syscall codes
const ( const (
SysMmap = 4090 SysMmap = 4090
...@@ -79,7 +87,7 @@ const ( ...@@ -79,7 +87,7 @@ const (
// Errors // Errors
const ( const (
SysErrorSignal = ^uint32(0) SysErrorSignal = ^Word(0)
MipsEBADF = 0x9 MipsEBADF = 0x9
MipsEINVAL = 0x16 MipsEINVAL = 0x16
MipsEAGAIN = 0xb MipsEAGAIN = 0xb
...@@ -92,7 +100,7 @@ const ( ...@@ -92,7 +100,7 @@ const (
FutexWakePrivate = 129 FutexWakePrivate = 129
FutexTimeoutSteps = 10_000 FutexTimeoutSteps = 10_000
FutexNoTimeout = ^uint64(0) FutexNoTimeout = ^uint64(0)
FutexEmptyAddr = ^uint32(0) FutexEmptyAddr = ^Word(0)
) )
// SysClone flags // SysClone flags
...@@ -145,7 +153,7 @@ const ( ...@@ -145,7 +153,7 @@ const (
ClockGettimeMonotonicFlag = 1 ClockGettimeMonotonicFlag = 1
) )
func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { func GetSyscallArgs(registers *[32]Word) (syscallNum, a0, a1, a2, a3 Word) {
syscallNum = registers[2] // v0 syscallNum = registers[2] // v0
a0 = registers[4] a0 = registers[4]
...@@ -156,8 +164,8 @@ func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { ...@@ -156,8 +164,8 @@ func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) {
return syscallNum, a0, a1, a2, a3 return syscallNum, a0, a1, a2, a3
} }
func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) {
v1 = uint32(0) v1 = Word(0)
newHeap = heap newHeap = heap
sz := a1 sz := a1
...@@ -182,34 +190,41 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { ...@@ -182,34 +190,41 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) {
return v0, v1, newHeap return v0, v1, newHeap
} }
func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint32, preimageReader PreimageReader, memory *memory.Memory, memTracker MemTracker) (v0, v1, newPreimageOffset uint32, memUpdated bool, memAddr uint32) { func HandleSysRead(
a0, a1, a2 Word,
preimageKey [32]byte,
preimageOffset Word,
preimageReader PreimageReader,
memory *memory.Memory,
memTracker MemTracker,
) (v0, v1, newPreimageOffset Word, memUpdated bool, memAddr Word) {
// args: a0 = fd, a1 = addr, a2 = count // args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = read, v1 = err code // returns: v0 = read, v1 = err code
v0 = uint32(0) v0 = Word(0)
v1 = uint32(0) v1 = Word(0)
newPreimageOffset = preimageOffset newPreimageOffset = preimageOffset
switch a0 { switch a0 {
case FdStdin: case FdStdin:
// leave v0 and v1 zero: read nothing, no error // leave v0 and v1 zero: read nothing, no error
case FdPreimageRead: // pre-image oracle case FdPreimageRead: // pre-image oracle
effAddr := a1 & 0xFFffFFfc effAddr := a1 & AddressMask
memTracker.TrackMemAccess(effAddr) memTracker.TrackMemAccess(effAddr)
mem := memory.GetMemory(effAddr) mem := memory.GetWord(effAddr)
dat, datLen := preimageReader.ReadPreimage(preimageKey, preimageOffset) dat, datLen := preimageReader.ReadPreimage(preimageKey, preimageOffset)
//fmt.Printf("reading pre-image data: addr: %08x, offset: %d, datLen: %d, data: %x, key: %s count: %d\n", a1, preimageOffset, datLen, dat[:datLen], preimageKey, a2) //fmt.Printf("reading pre-image data: addr: %08x, offset: %d, datLen: %d, data: %x, key: %s count: %d\n", a1, preimageOffset, datLen, dat[:datLen], preimageKey, a2)
alignment := a1 & 3 alignment := a1 & arch.ExtMask
space := 4 - alignment space := arch.WordSizeBytes - alignment
if space < datLen { if space < datLen {
datLen = space datLen = space
} }
if a2 < datLen { if a2 < datLen {
datLen = a2 datLen = a2
} }
var outMem [4]byte var outMem [arch.WordSizeBytes]byte
binary.BigEndian.PutUint32(outMem[:], mem) arch.ByteOrderWord.PutWord(outMem[:], mem)
copy(outMem[alignment:], dat[:datLen]) copy(outMem[alignment:], dat[:datLen])
memory.SetMemory(effAddr, binary.BigEndian.Uint32(outMem[:])) memory.SetWord(effAddr, arch.ByteOrderWord.Word(outMem[:]))
memUpdated = true memUpdated = true
memAddr = effAddr memAddr = effAddr
newPreimageOffset += datLen newPreimageOffset += datLen
...@@ -219,17 +234,25 @@ func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint3 ...@@ -219,17 +234,25 @@ func HandleSysRead(a0, a1, a2 uint32, preimageKey [32]byte, preimageOffset uint3
// don't actually read into memory, just say we read it all, we ignore the result anyway // don't actually read into memory, just say we read it all, we ignore the result anyway
v0 = a2 v0 = a2
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
return v0, v1, newPreimageOffset, memUpdated, memAddr return v0, v1, newPreimageOffset, memUpdated, memAddr
} }
func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]byte, preimageOffset uint32, oracle mipsevm.PreimageOracle, memory *memory.Memory, memTracker MemTracker, stdOut, stdErr io.Writer) (v0, v1 uint32, newLastHint hexutil.Bytes, newPreimageKey common.Hash, newPreimageOffset uint32) { func HandleSysWrite(a0, a1, a2 Word,
lastHint hexutil.Bytes,
preimageKey [32]byte,
preimageOffset Word,
oracle mipsevm.PreimageOracle,
memory *memory.Memory,
memTracker MemTracker,
stdOut, stdErr io.Writer,
) (v0, v1 Word, newLastHint hexutil.Bytes, newPreimageKey common.Hash, newPreimageOffset Word) {
// args: a0 = fd, a1 = addr, a2 = count // args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = written, v1 = err code // returns: v0 = written, v1 = err code
v1 = uint32(0) v1 = Word(0)
newLastHint = lastHint newLastHint = lastHint
newPreimageKey = preimageKey newPreimageKey = preimageKey
newPreimageOffset = preimageOffset newPreimageOffset = preimageOffset
...@@ -257,41 +280,41 @@ func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]b ...@@ -257,41 +280,41 @@ func HandleSysWrite(a0, a1, a2 uint32, lastHint hexutil.Bytes, preimageKey [32]b
newLastHint = lastHint newLastHint = lastHint
v0 = a2 v0 = a2
case FdPreimageWrite: case FdPreimageWrite:
effAddr := a1 & 0xFFffFFfc effAddr := a1 & arch.AddressMask
memTracker.TrackMemAccess(effAddr) memTracker.TrackMemAccess(effAddr)
mem := memory.GetMemory(effAddr) mem := memory.GetWord(effAddr)
key := preimageKey key := preimageKey
alignment := a1 & 3 alignment := a1 & arch.ExtMask
space := 4 - alignment space := arch.WordSizeBytes - alignment
if space < a2 { if space < a2 {
a2 = space a2 = space
} }
copy(key[:], key[a2:]) copy(key[:], key[a2:])
var tmp [4]byte var tmp [arch.WordSizeBytes]byte
binary.BigEndian.PutUint32(tmp[:], mem) arch.ByteOrderWord.PutWord(tmp[:], mem)
copy(key[32-a2:], tmp[alignment:]) copy(key[32-a2:], tmp[alignment:])
newPreimageKey = key newPreimageKey = key
newPreimageOffset = 0 newPreimageOffset = 0
//fmt.Printf("updating pre-image key: %s\n", m.state.PreimageKey) //fmt.Printf("updating pre-image key: %s\n", m.state.PreimageKey)
v0 = a2 v0 = a2
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
return v0, v1, newLastHint, newPreimageKey, newPreimageOffset return v0, v1, newLastHint, newPreimageKey, newPreimageOffset
} }
func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { func HandleSysFcntl(a0, a1 Word) (v0, v1 Word) {
// args: a0 = fd, a1 = cmd // args: a0 = fd, a1 = cmd
v1 = uint32(0) v1 = Word(0)
if a1 == 1 { // F_GETFD: get file descriptor flags if a1 == 1 { // F_GETFD: get file descriptor flags
switch a0 { switch a0 {
case FdStdin, FdStdout, FdStderr, FdPreimageRead, FdHintRead, FdPreimageWrite, FdHintWrite: case FdStdin, FdStdout, FdStderr, FdPreimageRead, FdHintRead, FdPreimageWrite, FdHintWrite:
v0 = 0 // No flags set v0 = 0 // No flags set
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
} else if a1 == 3 { // F_GETFL: get file status flags } else if a1 == 3 { // F_GETFL: get file status flags
...@@ -301,18 +324,18 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) { ...@@ -301,18 +324,18 @@ func HandleSysFcntl(a0, a1 uint32) (v0, v1 uint32) {
case FdStdout, FdStderr, FdPreimageWrite, FdHintWrite: case FdStdout, FdStderr, FdPreimageWrite, FdHintWrite:
v0 = 1 // O_WRONLY v0 = 1 // O_WRONLY
default: default:
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEBADF v1 = MipsEBADF
} }
} else { } else {
v0 = 0xFFffFFff v0 = ^Word(0)
v1 = MipsEINVAL // cmd not recognized by this kernel v1 = MipsEINVAL // cmd not recognized by this kernel
} }
return v0, v1 return v0, v1
} }
func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]uint32, v0, v1 uint32) { func HandleSyscallUpdates(cpu *mipsevm.CpuScalars, registers *[32]Word, v0, v1 Word) {
registers[2] = v0 registers[2] = v0
registers[7] = v1 registers[7] = v1
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
) )
type PreimageReader interface { type PreimageReader interface {
ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) ReadPreimage(key [32]byte, offset Word) (dat [32]byte, datLen Word)
} }
// TrackingPreimageOracleReader wraps around a PreimageOracle, implements the PreimageOracle interface, and adds tracking functionality. // TrackingPreimageOracleReader wraps around a PreimageOracle, implements the PreimageOracle interface, and adds tracking functionality.
...@@ -22,8 +22,8 @@ type TrackingPreimageOracleReader struct { ...@@ -22,8 +22,8 @@ type TrackingPreimageOracleReader struct {
lastPreimage []byte lastPreimage []byte
// key for above preimage // key for above preimage
lastPreimageKey [32]byte lastPreimageKey [32]byte
// offset we last read from, or max uint32 if nothing is read this step // offset we last read from, or max Word if nothing is read this step
lastPreimageOffset uint32 lastPreimageOffset Word
} }
func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimageOracleReader { func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimageOracleReader {
...@@ -31,7 +31,7 @@ func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimag ...@@ -31,7 +31,7 @@ func NewTrackingPreimageOracleReader(po mipsevm.PreimageOracle) *TrackingPreimag
} }
func (p *TrackingPreimageOracleReader) Reset() { func (p *TrackingPreimageOracleReader) Reset() {
p.lastPreimageOffset = ^uint32(0) p.lastPreimageOffset = ^Word(0)
} }
func (p *TrackingPreimageOracleReader) Hint(v []byte) { func (p *TrackingPreimageOracleReader) Hint(v []byte) {
...@@ -45,7 +45,7 @@ func (p *TrackingPreimageOracleReader) GetPreimage(k [32]byte) []byte { ...@@ -45,7 +45,7 @@ func (p *TrackingPreimageOracleReader) GetPreimage(k [32]byte) []byte {
return preimage return preimage
} }
func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) { func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset Word) (dat [32]byte, datLen Word) {
preimage := p.lastPreimage preimage := p.lastPreimage
if key != p.lastPreimageKey { if key != p.lastPreimageKey {
p.lastPreimageKey = key p.lastPreimageKey = key
...@@ -57,14 +57,14 @@ func (p *TrackingPreimageOracleReader) ReadPreimage(key [32]byte, offset uint32) ...@@ -57,14 +57,14 @@ 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 >= Word(len(preimage)) {
panic("Preimage offset out-of-bounds") panic("Preimage offset out-of-bounds")
} }
datLen = uint32(copy(dat[:], preimage[offset:])) datLen = Word(copy(dat[:], preimage[offset:]))
return return
} }
func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, uint32) { func (p *TrackingPreimageOracleReader) LastPreimage() ([32]byte, []byte, Word) {
return p.lastPreimageKey, p.lastPreimage, p.lastPreimageOffset return p.lastPreimageKey, p.lastPreimage, p.lastPreimageOffset
} }
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
) )
type StackTracker interface { type StackTracker interface {
PushStack(caller uint32, target uint32) PushStack(caller Word, target Word)
PopStack() PopStack()
} }
...@@ -19,7 +19,7 @@ type TraceableStackTracker interface { ...@@ -19,7 +19,7 @@ type TraceableStackTracker interface {
type NoopStackTracker struct{} type NoopStackTracker struct{}
func (n *NoopStackTracker) PushStack(caller uint32, target uint32) {} func (n *NoopStackTracker) PushStack(caller Word, target Word) {}
func (n *NoopStackTracker) PopStack() {} func (n *NoopStackTracker) PopStack() {}
...@@ -28,8 +28,8 @@ func (n *NoopStackTracker) Traceback() {} ...@@ -28,8 +28,8 @@ func (n *NoopStackTracker) Traceback() {}
type StackTrackerImpl struct { type StackTrackerImpl struct {
state mipsevm.FPVMState state mipsevm.FPVMState
stack []uint32 stack []Word
caller []uint32 caller []Word
meta mipsevm.Metadata meta mipsevm.Metadata
} }
...@@ -45,7 +45,7 @@ func NewStackTrackerUnsafe(state mipsevm.FPVMState, meta mipsevm.Metadata) *Stac ...@@ -45,7 +45,7 @@ func NewStackTrackerUnsafe(state mipsevm.FPVMState, meta mipsevm.Metadata) *Stac
return &StackTrackerImpl{state: state, meta: meta} return &StackTrackerImpl{state: state, meta: meta}
} }
func (s *StackTrackerImpl) PushStack(caller uint32, target uint32) { func (s *StackTrackerImpl) PushStack(caller Word, target Word) {
s.caller = append(s.caller, caller) s.caller = append(s.caller, caller)
s.stack = append(s.stack, target) s.stack = append(s.stack, target)
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -17,22 +18,22 @@ type FPVMState interface { ...@@ -17,22 +18,22 @@ type FPVMState interface {
GetMemory() *memory.Memory GetMemory() *memory.Memory
// GetHeap returns the current memory address at the top of the heap // GetHeap returns the current memory address at the top of the heap
GetHeap() uint32 GetHeap() arch.Word
// GetPreimageKey returns the most recently accessed preimage key // GetPreimageKey returns the most recently accessed preimage key
GetPreimageKey() common.Hash GetPreimageKey() common.Hash
// GetPreimageOffset returns the current offset into the current preimage // GetPreimageOffset returns the current offset into the current preimage
GetPreimageOffset() uint32 GetPreimageOffset() arch.Word
// GetPC returns the currently executing program counter // GetPC returns the currently executing program counter
GetPC() uint32 GetPC() arch.Word
// GetCpu returns the currently active cpu scalars, including the program counter // GetCpu returns the currently active cpu scalars, including the program counter
GetCpu() CpuScalars GetCpu() CpuScalars
// GetRegistersRef returns a pointer to the currently active registers // GetRegistersRef returns a pointer to the currently active registers
GetRegistersRef() *[32]uint32 GetRegistersRef() *[32]arch.Word
// GetStep returns the current VM step // GetStep returns the current VM step
GetStep() uint64 GetStep() uint64
...@@ -48,9 +49,9 @@ type FPVMState interface { ...@@ -48,9 +49,9 @@ type FPVMState interface {
// so a VM can start from any state without fetching prior pre-images, // so a VM can start from any state without fetching prior pre-images,
// and instead just repeat the last hint on setup, // and instead just repeat the last hint on setup,
// to make sure pre-image requests can be served. // to make sure pre-image requests can be served.
// The first 4 bytes are a uint32 length prefix. // The first 4 bytes are a Word length prefix.
// Warning: the hint MAY NOT BE COMPLETE. I.e. this is buffered, // 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:]) // and should only be read when len(LastHint) > 4 && Word(LastHint[:4]) <= len(LastHint[4:])
GetLastHint() hexutil.Bytes 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
...@@ -60,10 +61,10 @@ type FPVMState interface { ...@@ -60,10 +61,10 @@ type FPVMState interface {
CreateVM(logger log.Logger, po PreimageOracle, stdOut, stdErr io.Writer, meta Metadata) FPVM CreateVM(logger log.Logger, po PreimageOracle, stdOut, stdErr io.Writer, meta Metadata) FPVM
} }
type SymbolMatcher func(addr uint32) bool type SymbolMatcher func(addr arch.Word) bool
type Metadata interface { type Metadata interface {
LookupSymbol(addr uint32) string LookupSymbol(addr arch.Word) string
CreateSymbolMatcher(name string) SymbolMatcher CreateSymbolMatcher(name string) SymbolMatcher
} }
...@@ -78,7 +79,7 @@ type FPVM interface { ...@@ -78,7 +79,7 @@ type FPVM interface {
CheckInfiniteLoop() bool CheckInfiniteLoop() bool
// LastPreimage returns the last preimage accessed by the VM // LastPreimage returns the last preimage accessed by the VM
LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset uint32) LastPreimage() (preimageKey [32]byte, preimage []byte, preimageOffset arch.Word)
// Traceback prints a traceback of the program to the console // Traceback prints a traceback of the program to the console
Traceback() Traceback()
...@@ -91,5 +92,5 @@ type FPVM interface { ...@@ -91,5 +92,5 @@ type FPVM interface {
// LookupSymbol returns the symbol located at the specified address. // LookupSymbol returns the symbol located at the specified address.
// May return an empty string if there's no symbol table available. // May return an empty string if there's no symbol table available.
LookupSymbol(addr uint32) string LookupSymbol(addr arch.Word) string
} }
This diff is collapsed.
...@@ -118,7 +118,7 @@ func TestMemoryReadWrite(t *testing.T) { ...@@ -118,7 +118,7 @@ func TestMemoryReadWrite(t *testing.T) {
_, err := rand.Read(data[:]) _, err := rand.Read(data[:])
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data))) require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
for _, i := range []uint32{0, 4, 1000, 20_000 - 4} { for _, i := range []Word{0, 4, 1000, 20_000 - 4} {
v := m.GetMemory(i) v := m.GetMemory(i)
expected := binary.BigEndian.Uint32(data[i : i+4]) expected := binary.BigEndian.Uint32(data[i : i+4])
require.Equalf(t, expected, v, "read at %d", i) require.Equalf(t, expected, v, "read at %d", i)
...@@ -129,7 +129,7 @@ func TestMemoryReadWrite(t *testing.T) { ...@@ -129,7 +129,7 @@ func TestMemoryReadWrite(t *testing.T) {
m := NewMemory() m := NewMemory()
data := []byte(strings.Repeat("under the big bright yellow sun ", 40)) data := []byte(strings.Repeat("under the big bright yellow sun ", 40))
require.NoError(t, m.SetMemoryRange(0x1337, bytes.NewReader(data))) require.NoError(t, m.SetMemoryRange(0x1337, bytes.NewReader(data)))
res, err := io.ReadAll(m.ReadMemoryRange(0x1337-10, uint32(len(data)+20))) res, err := io.ReadAll(m.ReadMemoryRange(0x1337-10, Word(len(data)+20)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, make([]byte, 10), res[:10], "empty start") require.Equal(t, make([]byte, 10), res[:10], "empty start")
require.Equal(t, data, res[10:len(res)-10], "result") require.Equal(t, data, res[10:len(res)-10], "result")
......
...@@ -70,7 +70,7 @@ type CachedPage struct { ...@@ -70,7 +70,7 @@ type CachedPage struct {
Ok [PageSize / 32]bool Ok [PageSize / 32]bool
} }
func (p *CachedPage) Invalidate(pageAddr uint32) { func (p *CachedPage) invalidate(pageAddr Word) {
if pageAddr >= PageSize { if pageAddr >= PageSize {
panic("invalid page addr") panic("invalid page addr")
} }
......
...@@ -29,16 +29,16 @@ func TestCachedPage(t *testing.T) { ...@@ -29,16 +29,16 @@ func TestCachedPage(t *testing.T) {
post := p.MerkleRoot() post := p.MerkleRoot()
require.Equal(t, pre, post, "no change expected until cache is invalidated") require.Equal(t, pre, post, "no change expected until cache is invalidated")
p.Invalidate(42) p.invalidate(42)
post2 := p.MerkleRoot() post2 := p.MerkleRoot()
require.NotEqual(t, post, post2, "change after cache invalidation") require.NotEqual(t, post, post2, "change after cache invalidation")
p.Data[2000] = 0xef p.Data[2000] = 0xef
p.Invalidate(42) p.invalidate(42)
post3 := p.MerkleRoot() post3 := p.MerkleRoot()
require.Equal(t, post2, post3, "local invalidation is not global invalidation") require.Equal(t, post2, post3, "local invalidation is not global invalidation")
p.Invalidate(2000) p.invalidate(2000)
post4 := p.MerkleRoot() post4 := p.MerkleRoot()
require.NotEqual(t, post3, post4, "can see the change now") require.NotEqual(t, post3, post4, "can see the change now")
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
) )
...@@ -77,7 +78,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro ...@@ -77,7 +78,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro
wit.ProofData = append(wit.ProofData, memProof[:]...) wit.ProofData = append(wit.ProofData, memProof[:]...)
wit.ProofData = append(wit.ProofData, memProof2[:]...) wit.ProofData = append(wit.ProofData, memProof2[:]...)
lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^arch.Word(0) {
wit.PreimageOffset = lastPreimageOffset wit.PreimageOffset = lastPreimageOffset
wit.PreimageKey = lastPreimageKey wit.PreimageKey = lastPreimageKey
wit.PreimageValue = lastPreimage wit.PreimageValue = lastPreimage
...@@ -90,7 +91,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { ...@@ -90,7 +91,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool {
return false return false
} }
func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, arch.Word) {
return m.preimageOracle.LastPreimage() return m.preimageOracle.LastPreimage()
} }
...@@ -111,7 +112,7 @@ func (m *InstrumentedState) Traceback() { ...@@ -111,7 +112,7 @@ func (m *InstrumentedState) Traceback() {
m.stackTracker.Traceback() m.stackTracker.Traceback()
} }
func (m *InstrumentedState) LookupSymbol(addr uint32) string { func (m *InstrumentedState) LookupSymbol(addr arch.Word) string {
if m.meta == nil { if m.meta == nil {
return "" return ""
} }
......
...@@ -20,7 +20,6 @@ func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer ...@@ -20,7 +20,6 @@ func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer
func TestInstrumentedState_OpenMips(t *testing.T) { func TestInstrumentedState_OpenMips(t *testing.T) {
t.Parallel() t.Parallel()
// TODO: Add mt-specific tests here
testutil.RunVMTests_OpenMips(t, CreateEmptyState, vmFactory, "clone.bin") testutil.RunVMTests_OpenMips(t, CreateEmptyState, vmFactory, "clone.bin")
} }
......
...@@ -9,21 +9,24 @@ import ( ...@@ -9,21 +9,24 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type Word = arch.Word
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.GetRegistersRef()) syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegistersRef())
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
//fmt.Printf("syscall: %d\n", syscallNum) //fmt.Printf("syscall: %d\n", syscallNum)
switch syscallNum { switch syscallNum {
case exec.SysMmap: case exec.SysMmap:
var newHeap uint32 var newHeap Word
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
...@@ -74,9 +77,9 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -74,9 +77,9 @@ func (m *InstrumentedState) handleSyscall() error {
m.state.ExitCode = uint8(a0) m.state.ExitCode = uint8(a0)
return nil return nil
case exec.SysRead: case exec.SysRead:
var newPreimageOffset uint32 var newPreimageOffset Word
var memUpdated bool var memUpdated bool
var memAddr uint32 var memAddr Word
v0, v1, newPreimageOffset, memUpdated, memAddr = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker) v0, v1, newPreimageOffset, memUpdated, memAddr = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker)
m.state.PreimageOffset = newPreimageOffset m.state.PreimageOffset = newPreimageOffset
if memUpdated { if memUpdated {
...@@ -85,7 +88,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -85,7 +88,7 @@ func (m *InstrumentedState) handleSyscall() error {
case exec.SysWrite: case exec.SysWrite:
var newLastHint hexutil.Bytes var newLastHint hexutil.Bytes
var newPreimageKey common.Hash var newPreimageKey common.Hash
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr) v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr)
m.state.LastHint = newLastHint m.state.LastHint = newLastHint
m.state.PreimageKey = newPreimageKey m.state.PreimageKey = newPreimageKey
...@@ -105,11 +108,11 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -105,11 +108,11 @@ func (m *InstrumentedState) handleSyscall() error {
return nil return nil
case exec.SysFutex: case exec.SysFutex:
// args: a0 = addr, a1 = op, a2 = val, a3 = timeout // args: a0 = addr, a1 = op, a2 = val, a3 = timeout
effAddr := a0 & 0xFFffFFfc effAddr := a0 & arch.AddressMask
switch a1 { switch a1 {
case exec.FutexWaitPrivate: case exec.FutexWaitPrivate:
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
if mem != a2 { if mem != a2 {
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsEAGAIN v1 = exec.MipsEAGAIN
...@@ -153,20 +156,20 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -153,20 +156,20 @@ func (m *InstrumentedState) handleSyscall() error {
switch a0 { switch a0 {
case exec.ClockGettimeRealtimeFlag, exec.ClockGettimeMonotonicFlag: case exec.ClockGettimeRealtimeFlag, exec.ClockGettimeMonotonicFlag:
v0, v1 = 0, 0 v0, v1 = 0, 0
var secs, nsecs uint32 var secs, nsecs Word
if a0 == exec.ClockGettimeMonotonicFlag { if a0 == exec.ClockGettimeMonotonicFlag {
// monotonic clock_gettime is used by Go guest programs for goroutine scheduling and to implement // monotonic clock_gettime is used by Go guest programs for goroutine scheduling and to implement
// `time.Sleep` (and other sleep related operations). // `time.Sleep` (and other sleep related operations).
secs = uint32(m.state.Step / exec.HZ) secs = Word(m.state.Step / exec.HZ)
nsecs = uint32((m.state.Step % exec.HZ) * (1_000_000_000 / exec.HZ)) nsecs = Word((m.state.Step % exec.HZ) * (1_000_000_000 / exec.HZ))
} // else realtime set to Unix Epoch } // else realtime set to Unix Epoch
effAddr := a1 & 0xFFffFFfc effAddr := a1 & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
m.state.Memory.SetMemory(effAddr, secs) m.state.Memory.SetWord(effAddr, secs)
m.handleMemoryUpdate(effAddr) m.handleMemoryUpdate(effAddr)
m.memoryTracker.TrackMemAccess2(effAddr + 4) m.memoryTracker.TrackMemAccess2(effAddr + 4)
m.state.Memory.SetMemory(effAddr+4, nsecs) m.state.Memory.SetWord(effAddr+4, nsecs)
m.handleMemoryUpdate(effAddr + 4) m.handleMemoryUpdate(effAddr + 4)
default: default:
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
...@@ -182,6 +185,8 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -182,6 +185,8 @@ func (m *InstrumentedState) handleSyscall() error {
case exec.SysSigaltstack: case exec.SysSigaltstack:
case exec.SysRtSigaction: case exec.SysRtSigaction:
case exec.SysPrlimit64: case exec.SysPrlimit64:
// TODO(#12205): may be needed for 64-bit Cannon
// case exec.SysGetRtLimit:
case exec.SysClose: case exec.SysClose:
case exec.SysPread64: case exec.SysPread64:
case exec.SysFstat64: case exec.SysFstat64:
...@@ -256,9 +261,9 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -256,9 +261,9 @@ func (m *InstrumentedState) mipsStep() error {
m.onWaitComplete(thread, true) m.onWaitComplete(thread, true)
return nil return nil
} else { } else {
effAddr := thread.FutexAddr & 0xFFffFFfc effAddr := thread.FutexAddr & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
if thread.FutexVal == mem { if thread.FutexVal == mem {
// still got expected value, continue sleeping, try next thread. // still got expected value, continue sleeping, try next thread.
m.preemptThread(thread) m.preemptThread(thread)
...@@ -299,6 +304,12 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -299,6 +304,12 @@ func (m *InstrumentedState) mipsStep() error {
if opcode == exec.OpLoadLinked || opcode == exec.OpStoreConditional { if opcode == exec.OpLoadLinked || opcode == exec.OpStoreConditional {
return m.handleRMWOps(insn, opcode) return m.handleRMWOps(insn, opcode)
} }
if opcode == exec.OpLoadLinked64 || opcode == exec.OpStoreConditional64 {
if arch.IsMips32 {
panic(fmt.Sprintf("invalid instruction: %x", insn))
}
return m.handleRMWOps(insn, opcode)
}
// Exec the rest of the step logic // Exec the rest of the step logic
memUpdated, memAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) memUpdated, memAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker)
...@@ -312,7 +323,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -312,7 +323,7 @@ func (m *InstrumentedState) mipsStep() error {
return nil return nil
} }
func (m *InstrumentedState) handleMemoryUpdate(memAddr uint32) { func (m *InstrumentedState) handleMemoryUpdate(memAddr Word) {
if memAddr == m.state.LLAddress { if memAddr == m.state.LLAddress {
// Reserved address was modified, clear the reservation // Reserved address was modified, clear the reservation
m.clearLLMemoryReservation() m.clearLLMemoryReservation()
...@@ -329,27 +340,32 @@ func (m *InstrumentedState) clearLLMemoryReservation() { ...@@ -329,27 +340,32 @@ func (m *InstrumentedState) clearLLMemoryReservation() {
func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error { func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error {
baseReg := (insn >> 21) & 0x1F baseReg := (insn >> 21) & 0x1F
base := m.state.GetRegistersRef()[baseReg] base := m.state.GetRegistersRef()[baseReg]
rtReg := (insn >> 16) & 0x1F rtReg := Word((insn >> 16) & 0x1F)
offset := exec.SignExtendImmediate(insn) offset := exec.SignExtendImmediate(insn)
effAddr := (base + offset) & 0xFFFFFFFC effAddr := (base + offset) & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
var retVal uint32 var retVal Word
threadId := m.state.GetCurrentThread().ThreadId threadId := m.state.GetCurrentThread().ThreadId
if opcode == exec.OpLoadLinked { if opcode == exec.OpLoadLinked || opcode == exec.OpLoadLinked64 {
retVal = mem retVal = mem
m.state.LLReservationActive = true m.state.LLReservationActive = true
m.state.LLAddress = effAddr m.state.LLAddress = effAddr
m.state.LLOwnerThread = threadId m.state.LLOwnerThread = threadId
} else if opcode == exec.OpStoreConditional { } else if opcode == exec.OpStoreConditional || opcode == exec.OpStoreConditional64 {
// TODO(#12205): Determine bits affected by coherence stores on 64-bits
// Check if our memory reservation is still intact // Check if our memory reservation is still intact
if m.state.LLReservationActive && m.state.LLOwnerThread == threadId && m.state.LLAddress == effAddr { if m.state.LLReservationActive && m.state.LLOwnerThread == threadId && m.state.LLAddress == effAddr {
// Complete atomic update: set memory and return 1 for success // Complete atomic update: set memory and return 1 for success
m.clearLLMemoryReservation() m.clearLLMemoryReservation()
rt := m.state.GetRegistersRef()[rtReg] rt := m.state.GetRegistersRef()[rtReg]
m.state.Memory.SetMemory(effAddr, rt) if opcode == exec.OpStoreConditional {
m.state.Memory.SetMemory(effAddr, uint32(rt))
} else {
m.state.Memory.SetWord(effAddr, rt)
}
retVal = 1 retVal = 1
} else { } else {
// Atomic update failed, return 0 for failure // Atomic update failed, return 0 for failure
...@@ -370,8 +386,8 @@ func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) ...@@ -370,8 +386,8 @@ func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool)
thread.FutexTimeoutStep = 0 thread.FutexTimeoutStep = 0
// Complete the FUTEX_WAIT syscall // Complete the FUTEX_WAIT syscall
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
if isTimedOut { if isTimedOut {
v0 = exec.SysErrorSignal v0 = exec.SysErrorSignal
v1 = exec.MipsETIMEDOUT v1 = exec.MipsETIMEDOUT
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
type ThreadedStackTracker interface { type ThreadedStackTracker interface {
exec.TraceableStackTracker exec.TraceableStackTracker
DropThread(threadId uint32) DropThread(threadId Word)
} }
type NoopThreadedStackTracker struct { type NoopThreadedStackTracker struct {
...@@ -18,12 +18,12 @@ type NoopThreadedStackTracker struct { ...@@ -18,12 +18,12 @@ type NoopThreadedStackTracker struct {
var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil)
func (n *NoopThreadedStackTracker) DropThread(threadId uint32) {} func (n *NoopThreadedStackTracker) DropThread(threadId Word) {}
type ThreadedStackTrackerImpl struct { type ThreadedStackTrackerImpl struct {
meta mipsevm.Metadata meta mipsevm.Metadata
state *State state *State
trackersByThreadId map[uint32]exec.TraceableStackTracker trackersByThreadId map[Word]exec.TraceableStackTracker
} }
var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil) var _ ThreadedStackTracker = (*ThreadedStackTrackerImpl)(nil)
...@@ -36,11 +36,11 @@ func NewThreadedStackTracker(state *State, meta mipsevm.Metadata) (*ThreadedStac ...@@ -36,11 +36,11 @@ func NewThreadedStackTracker(state *State, meta mipsevm.Metadata) (*ThreadedStac
return &ThreadedStackTrackerImpl{ return &ThreadedStackTrackerImpl{
state: state, state: state,
meta: meta, meta: meta,
trackersByThreadId: make(map[uint32]exec.TraceableStackTracker), trackersByThreadId: make(map[Word]exec.TraceableStackTracker),
}, nil }, nil
} }
func (t *ThreadedStackTrackerImpl) PushStack(caller uint32, target uint32) { func (t *ThreadedStackTrackerImpl) PushStack(caller Word, target Word) {
t.getCurrentTracker().PushStack(caller, target) t.getCurrentTracker().PushStack(caller, target)
} }
...@@ -62,6 +62,6 @@ func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracke ...@@ -62,6 +62,6 @@ func (t *ThreadedStackTrackerImpl) getCurrentTracker() exec.TraceableStackTracke
return tracker return tracker
} }
func (t *ThreadedStackTrackerImpl) DropThread(threadId uint32) { func (t *ThreadedStackTrackerImpl) DropThread(threadId Word) {
delete(t.trackersByThreadId, threadId) delete(t.trackersByThreadId, threadId)
} }
...@@ -11,54 +11,57 @@ import ( ...@@ -11,54 +11,57 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"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/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
) )
// STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes.
const STATE_WITNESS_SIZE = 172
const ( const (
MEMROOT_WITNESS_OFFSET = 0 MEMROOT_WITNESS_OFFSET = 0
PREIMAGE_KEY_WITNESS_OFFSET = MEMROOT_WITNESS_OFFSET + 32 PREIMAGE_KEY_WITNESS_OFFSET = MEMROOT_WITNESS_OFFSET + 32
PREIMAGE_OFFSET_WITNESS_OFFSET = PREIMAGE_KEY_WITNESS_OFFSET + 32 PREIMAGE_OFFSET_WITNESS_OFFSET = PREIMAGE_KEY_WITNESS_OFFSET + 32
HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + 4 HEAP_WITNESS_OFFSET = PREIMAGE_OFFSET_WITNESS_OFFSET + arch.WordSizeBytes
LL_RESERVATION_ACTIVE_OFFSET = HEAP_WITNESS_OFFSET + 4 LL_RESERVATION_ACTIVE_OFFSET = HEAP_WITNESS_OFFSET + arch.WordSizeBytes
LL_ADDRESS_OFFSET = LL_RESERVATION_ACTIVE_OFFSET + 1 LL_ADDRESS_OFFSET = LL_RESERVATION_ACTIVE_OFFSET + 1
LL_OWNER_THREAD_OFFSET = LL_ADDRESS_OFFSET + 4 LL_OWNER_THREAD_OFFSET = LL_ADDRESS_OFFSET + arch.WordSizeBytes
EXITCODE_WITNESS_OFFSET = LL_OWNER_THREAD_OFFSET + 4 EXITCODE_WITNESS_OFFSET = LL_OWNER_THREAD_OFFSET + arch.WordSizeBytes
EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1 EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1
STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1 STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1
STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_WITNESS_OFFSET + 8 STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_WITNESS_OFFSET + 8
WAKEUP_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8 WAKEUP_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8
TRAVERSE_RIGHT_WITNESS_OFFSET = WAKEUP_WITNESS_OFFSET + 4 TRAVERSE_RIGHT_WITNESS_OFFSET = WAKEUP_WITNESS_OFFSET + arch.WordSizeBytes
LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1 LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1
RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32 RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32
THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32 THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32
// 172 and 196 bytes for 32 and 64-bit respectively
STATE_WITNESS_SIZE = THREAD_ID_WITNESS_OFFSET + arch.WordSizeBytes
) )
type State struct { type State struct {
Memory *memory.Memory Memory *memory.Memory
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 // note that the offset includes the 8-byte length prefix PreimageOffset Word // note that the offset includes the 8-byte length prefix
Heap uint32 // to handle mmap growth Heap Word // to handle mmap growth
LLReservationActive bool // Whether there is an active memory reservation initiated via the LL (load linked) op LLReservationActive bool // Whether there is an active memory reservation initiated via the LL (load linked) op
LLAddress uint32 // The "linked" memory address reserved via the LL (load linked) op LLAddress Word // The "linked" memory address reserved via the LL (load linked) op
LLOwnerThread uint32 // The id of the thread that holds the reservation on LLAddress LLOwnerThread Word // The id of the thread that holds the reservation on LLAddress
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
StepsSinceLastContextSwitch uint64 StepsSinceLastContextSwitch uint64
Wakeup uint32 Wakeup Word
TraverseRight bool TraverseRight bool
LeftThreadStack []*ThreadState LeftThreadStack []*ThreadState
RightThreadStack []*ThreadState RightThreadStack []*ThreadState
NextThreadId uint32 NextThreadId Word
// LastHint is optional metadata, and not part of the VM state itself. // LastHint is optional metadata, and not part of the VM state itself.
LastHint hexutil.Bytes LastHint hexutil.Bytes
...@@ -86,7 +89,7 @@ func CreateEmptyState() *State { ...@@ -86,7 +89,7 @@ func CreateEmptyState() *State {
} }
} }
func CreateInitialState(pc, heapStart uint32) *State { func CreateInitialState(pc, heapStart Word) *State {
state := CreateEmptyState() state := CreateEmptyState()
currentThread := state.GetCurrentThread() currentThread := state.GetCurrentThread()
currentThread.Cpu.PC = pc currentThread.Cpu.PC = pc
...@@ -97,6 +100,7 @@ func CreateInitialState(pc, heapStart uint32) *State { ...@@ -97,6 +100,7 @@ func CreateInitialState(pc, heapStart uint32) *State {
} }
func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM { func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) mipsevm.FPVM {
logger.Info("Using cannon multithreaded VM", "is32", arch.IsMips32)
return NewInstrumentedState(s, po, stdOut, stdErr, logger, meta) return NewInstrumentedState(s, po, stdOut, stdErr, logger, meta)
} }
...@@ -139,7 +143,7 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash { ...@@ -139,7 +143,7 @@ func (s *State) calculateThreadStackRoot(stack []*ThreadState) common.Hash {
return curRoot return curRoot
} }
func (s *State) GetPC() uint32 { func (s *State) GetPC() Word {
activeThread := s.GetCurrentThread() activeThread := s.GetCurrentThread()
return activeThread.Cpu.PC return activeThread.Cpu.PC
} }
...@@ -153,7 +157,7 @@ func (s *State) getCpuRef() *mipsevm.CpuScalars { ...@@ -153,7 +157,7 @@ func (s *State) getCpuRef() *mipsevm.CpuScalars {
return &s.GetCurrentThread().Cpu return &s.GetCurrentThread().Cpu
} }
func (s *State) GetRegistersRef() *[32]uint32 { func (s *State) GetRegistersRef() *[32]Word {
activeThread := s.GetCurrentThread() activeThread := s.GetCurrentThread()
return &activeThread.Registers return &activeThread.Registers
} }
...@@ -176,7 +180,7 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -176,7 +180,7 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 { func (s *State) GetHeap() Word {
return s.Heap return s.Heap
} }
...@@ -184,7 +188,7 @@ func (s *State) GetPreimageKey() common.Hash { ...@@ -184,7 +188,7 @@ func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey return s.PreimageKey
} }
func (s *State) GetPreimageOffset() uint32 { func (s *State) GetPreimageOffset() Word {
return s.PreimageOffset return s.PreimageOffset
} }
...@@ -193,24 +197,24 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -193,24 +197,24 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
out = append(out, memRoot[:]...) out = append(out, memRoot[:]...)
out = append(out, s.PreimageKey[:]...) out = append(out, s.PreimageKey[:]...)
out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) out = arch.ByteOrderWord.AppendWord(out, s.PreimageOffset)
out = binary.BigEndian.AppendUint32(out, s.Heap) out = arch.ByteOrderWord.AppendWord(out, s.Heap)
out = mipsevm.AppendBoolToWitness(out, s.LLReservationActive) out = mipsevm.AppendBoolToWitness(out, s.LLReservationActive)
out = binary.BigEndian.AppendUint32(out, s.LLAddress) out = arch.ByteOrderWord.AppendWord(out, s.LLAddress)
out = binary.BigEndian.AppendUint32(out, s.LLOwnerThread) out = arch.ByteOrderWord.AppendWord(out, s.LLOwnerThread)
out = append(out, s.ExitCode) out = append(out, s.ExitCode)
out = mipsevm.AppendBoolToWitness(out, s.Exited) out = mipsevm.AppendBoolToWitness(out, s.Exited)
out = binary.BigEndian.AppendUint64(out, s.Step) out = binary.BigEndian.AppendUint64(out, s.Step)
out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch) out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch)
out = binary.BigEndian.AppendUint32(out, s.Wakeup) out = arch.ByteOrderWord.AppendWord(out, s.Wakeup)
leftStackRoot := s.getLeftThreadStackRoot() leftStackRoot := s.getLeftThreadStackRoot()
rightStackRoot := s.getRightThreadStackRoot() rightStackRoot := s.getRightThreadStackRoot()
out = mipsevm.AppendBoolToWitness(out, s.TraverseRight) out = mipsevm.AppendBoolToWitness(out, s.TraverseRight)
out = append(out, (leftStackRoot)[:]...) out = append(out, (leftStackRoot)[:]...)
out = append(out, (rightStackRoot)[:]...) out = append(out, (rightStackRoot)[:]...)
out = binary.BigEndian.AppendUint32(out, s.NextThreadId) out = arch.ByteOrderWord.AppendWord(out, s.NextThreadId)
return out, stateHashFromWitness(out) return out, stateHashFromWitness(out)
} }
...@@ -245,20 +249,20 @@ func (s *State) ThreadCount() int { ...@@ -245,20 +249,20 @@ func (s *State) ThreadCount() int {
// StateVersion uint8(1) // StateVersion uint8(1)
// Memory As per Memory.Serialize // Memory As per Memory.Serialize
// PreimageKey [32]byte // PreimageKey [32]byte
// PreimageOffset uint32 // PreimageOffset Word
// Heap uint32 // Heap Word
// ExitCode uint8 // ExitCode uint8
// Exited uint8 - 0 for false, 1 for true // Exited uint8 - 0 for false, 1 for true
// Step uint64 // Step uint64
// StepsSinceLastContextSwitch uint64 // StepsSinceLastContextSwitch uint64
// Wakeup uint32 // Wakeup Word
// TraverseRight uint8 - 0 for false, 1 for true // TraverseRight uint8 - 0 for false, 1 for true
// NextThreadId uint32 // NextThreadId Word
// len(LeftThreadStack) uint32 // len(LeftThreadStack) Word
// LeftThreadStack entries as per ThreadState.Serialize // LeftThreadStack entries as per ThreadState.Serialize
// len(RightThreadStack) uint32 // len(RightThreadStack) Word
// RightThreadStack entries as per ThreadState.Serialize // RightThreadStack entries as per ThreadState.Serialize
// len(LastHint) uint32 (0 when LastHint is nil) // len(LastHint) Word (0 when LastHint is nil)
// LastHint []byte // LastHint []byte
func (s *State) Serialize(out io.Writer) error { func (s *State) Serialize(out io.Writer) error {
bout := serialize.NewBinaryWriter(out) bout := serialize.NewBinaryWriter(out)
...@@ -306,7 +310,7 @@ func (s *State) Serialize(out io.Writer) error { ...@@ -306,7 +310,7 @@ func (s *State) Serialize(out io.Writer) error {
return err return err
} }
if err := bout.WriteUInt(uint32(len(s.LeftThreadStack))); err != nil { if err := bout.WriteUInt(Word(len(s.LeftThreadStack))); err != nil {
return err return err
} }
for _, stack := range s.LeftThreadStack { for _, stack := range s.LeftThreadStack {
...@@ -314,7 +318,7 @@ func (s *State) Serialize(out io.Writer) error { ...@@ -314,7 +318,7 @@ func (s *State) Serialize(out io.Writer) error {
return err return err
} }
} }
if err := bout.WriteUInt(uint32(len(s.RightThreadStack))); err != nil { if err := bout.WriteUInt(Word(len(s.RightThreadStack))); err != nil {
return err return err
} }
for _, stack := range s.RightThreadStack { for _, stack := range s.RightThreadStack {
...@@ -376,7 +380,7 @@ func (s *State) Deserialize(in io.Reader) error { ...@@ -376,7 +380,7 @@ func (s *State) Deserialize(in io.Reader) error {
return err return err
} }
var leftThreadStackSize uint32 var leftThreadStackSize Word
if err := bin.ReadUInt(&leftThreadStackSize); err != nil { if err := bin.ReadUInt(&leftThreadStackSize); err != nil {
return err return err
} }
...@@ -388,7 +392,7 @@ func (s *State) Deserialize(in io.Reader) error { ...@@ -388,7 +392,7 @@ func (s *State) Deserialize(in io.Reader) error {
} }
} }
var rightThreadStackSize uint32 var rightThreadStackSize Word
if err := bin.ReadUInt(&rightThreadStackSize); err != nil { if err := bin.ReadUInt(&rightThreadStackSize); err != nil {
return err return err
} }
...@@ -423,7 +427,7 @@ func GetStateHashFn() mipsevm.HashFn { ...@@ -423,7 +427,7 @@ func GetStateHashFn() mipsevm.HashFn {
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(fmt.Sprintf("Invalid witness length. Got %d, expected %d", len(sw), STATE_WITNESS_SIZE))
} }
hash := crypto.Keccak256Hash(sw) hash := crypto.Keccak256Hash(sw)
exitCode := sw[EXITCODE_WITNESS_OFFSET] exitCode := sw[EXITCODE_WITNESS_OFFSET]
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,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/arch"
"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"
...@@ -41,11 +42,11 @@ func TestState_EncodeWitness(t *testing.T) { ...@@ -41,11 +42,11 @@ func TestState_EncodeWitness(t *testing.T) {
{exited: true, exitCode: 3}, {exited: true, exitCode: 3},
} }
heap := uint32(12) heap := Word(12)
llAddress := uint32(55) llAddress := Word(55)
llThreadOwner := uint32(99) llThreadOwner := Word(99)
preimageKey := crypto.Keccak256Hash([]byte{1, 2, 3, 4}) preimageKey := crypto.Keccak256Hash([]byte{1, 2, 3, 4})
preimageOffset := uint32(24) preimageOffset := Word(24)
step := uint64(33) step := uint64(33)
stepsSinceContextSwitch := uint64(123) stepsSinceContextSwitch := uint64(123)
for _, c := range cases { for _, c := range cases {
...@@ -207,7 +208,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -207,7 +208,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xbeef, LO: 0xbeef,
HI: 0xbabe, HI: 0xbabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0xdeadbeef, 0xdeadbeef,
0xdeadbeef, 0xdeadbeef,
0xc0ffee, 0xc0ffee,
...@@ -230,7 +231,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -230,7 +231,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xeeef, LO: 0xeeef,
HI: 0xeabe, HI: 0xeabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0xabcdef, 0xabcdef,
0x123456, 0x123456,
}, },
...@@ -250,7 +251,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -250,7 +251,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xdeef, LO: 0xdeef,
HI: 0xdabe, HI: 0xdabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0x654321, 0x654321,
}, },
}, },
...@@ -267,7 +268,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -267,7 +268,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
LO: 0xceef, LO: 0xceef,
HI: 0xcabe, HI: 0xcabe,
}, },
Registers: [32]uint32{ Registers: [32]Word{
0x987653, 0x987653,
0xfedbca, 0xfedbca,
}, },
...@@ -302,7 +303,7 @@ func TestState_EncodeThreadProof_SingleThread(t *testing.T) { ...@@ -302,7 +303,7 @@ func TestState_EncodeThreadProof_SingleThread(t *testing.T) {
activeThread.Cpu.HI = 11 activeThread.Cpu.HI = 11
activeThread.Cpu.LO = 22 activeThread.Cpu.LO = 22
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
activeThread.Registers[i] = uint32(i) activeThread.Registers[i] = Word(i)
} }
expectedProof := append([]byte{}, activeThread.serializeThread()[:]...) expectedProof := append([]byte{}, activeThread.serializeThread()[:]...)
...@@ -324,12 +325,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { ...@@ -324,12 +325,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
// Set some fields on our threads // Set some fields on our threads
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
curThread := state.LeftThreadStack[i] curThread := state.LeftThreadStack[i]
curThread.Cpu.PC = uint32(4 * i) curThread.Cpu.PC = Word(4 * i)
curThread.Cpu.NextPC = curThread.Cpu.PC + 4 curThread.Cpu.NextPC = curThread.Cpu.PC + 4
curThread.Cpu.HI = uint32(11 + i) curThread.Cpu.HI = Word(11 + i)
curThread.Cpu.LO = uint32(22 + i) curThread.Cpu.LO = Word(22 + i)
for j := 0; j < 32; j++ { for j := 0; j < 32; j++ {
curThread.Registers[j] = uint32(j + i) curThread.Registers[j] = Word(j + i)
} }
} }
...@@ -355,12 +356,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) { ...@@ -355,12 +356,12 @@ func TestState_EncodeThreadProof_MultipleThreads(t *testing.T) {
func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) { func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
wakeupAddr uint32 wakeupAddr Word
traverseRight bool traverseRight bool
}{ }{
{"traverse left during wakeup traversal", uint32(99), false}, {"traverse left during wakeup traversal", Word(99), false},
{"traverse left during normal traversal", exec.FutexEmptyAddr, false}, {"traverse left during normal traversal", exec.FutexEmptyAddr, false},
{"traverse right during wakeup traversal", uint32(99), true}, {"traverse right during wakeup traversal", Word(99), true},
{"traverse right during normal traversal", exec.FutexEmptyAddr, true}, {"traverse right during normal traversal", exec.FutexEmptyAddr, true},
} }
...@@ -382,3 +383,19 @@ func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) { ...@@ -382,3 +383,19 @@ func TestState_EncodeThreadProof_EmptyThreadStackPanic(t *testing.T) {
}) })
} }
} }
func TestStateWitnessSize(t *testing.T) {
expectedWitnessSize := 172
if !arch.IsMips32 {
expectedWitnessSize = 196
}
require.Equal(t, expectedWitnessSize, STATE_WITNESS_SIZE)
}
func TestThreadStateWitnessSize(t *testing.T) {
expectedWitnessSize := 166
if !arch.IsMips32 {
expectedWitnessSize = 322
}
require.Equal(t, expectedWitnessSize, SERIALIZED_THREAD_SIZE)
}
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
) )
...@@ -15,11 +16,11 @@ import ( ...@@ -15,11 +16,11 @@ import (
// to define an expected post-state. The post-state is then validated with ExpectedMTState.Validate(t, postState) // to define an expected post-state. The post-state is then validated with ExpectedMTState.Validate(t, postState)
type ExpectedMTState struct { type ExpectedMTState struct {
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 PreimageOffset arch.Word
Heap uint32 Heap arch.Word
LLReservationActive bool LLReservationActive bool
LLAddress uint32 LLAddress arch.Word
LLOwnerThread uint32 LLOwnerThread arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
...@@ -28,37 +29,37 @@ type ExpectedMTState struct { ...@@ -28,37 +29,37 @@ type ExpectedMTState struct {
expectedMemory *memory.Memory expectedMemory *memory.Memory
// Threading-related expectations // Threading-related expectations
StepsSinceLastContextSwitch uint64 StepsSinceLastContextSwitch uint64
Wakeup uint32 Wakeup arch.Word
TraverseRight bool TraverseRight bool
NextThreadId uint32 NextThreadId arch.Word
ThreadCount int ThreadCount int
RightStackSize int RightStackSize int
LeftStackSize int LeftStackSize int
prestateActiveThreadId uint32 prestateActiveThreadId arch.Word
prestateActiveThreadOrig ExpectedThreadState // Cached for internal use prestateActiveThreadOrig ExpectedThreadState // Cached for internal use
ActiveThreadId uint32 ActiveThreadId arch.Word
threadExpectations map[uint32]*ExpectedThreadState threadExpectations map[arch.Word]*ExpectedThreadState
} }
type ExpectedThreadState struct { type ExpectedThreadState struct {
ThreadId uint32 ThreadId arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
FutexAddr uint32 FutexAddr arch.Word
FutexVal uint32 FutexVal arch.Word
FutexTimeoutStep uint64 FutexTimeoutStep uint64
PC uint32 PC arch.Word
NextPC uint32 NextPC arch.Word
HI uint32 HI arch.Word
LO uint32 LO arch.Word
Registers [32]uint32 Registers [32]arch.Word
Dropped bool Dropped bool
} }
func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState { func NewExpectedMTState(fromState *multithreaded.State) *ExpectedMTState {
currentThread := fromState.GetCurrentThread() currentThread := fromState.GetCurrentThread()
expectedThreads := make(map[uint32]*ExpectedThreadState) expectedThreads := make(map[arch.Word]*ExpectedThreadState)
for _, t := range GetAllThreads(fromState) { for _, t := range GetAllThreads(fromState) {
expectedThreads[t.ThreadId] = newExpectedThreadState(t) expectedThreads[t.ThreadId] = newExpectedThreadState(t)
} }
...@@ -118,12 +119,17 @@ func (e *ExpectedMTState) ExpectStep() { ...@@ -118,12 +119,17 @@ func (e *ExpectedMTState) ExpectStep() {
e.StepsSinceLastContextSwitch += 1 e.StepsSinceLastContextSwitch += 1
} }
func (e *ExpectedMTState) ExpectMemoryWrite(addr uint32, val uint32) { func (e *ExpectedMTState) ExpectMemoryWrite(addr arch.Word, val uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr uint32, val uint32, addr2 uint32, val2 uint32) { func (e *ExpectedMTState) ExpectMemoryWordWrite(addr arch.Word, val arch.Word) {
e.expectedMemory.SetWord(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot()
}
func (e *ExpectedMTState) ExpectMemoryWriteMultiple(addr arch.Word, val uint32, addr2 arch.Word, val2 uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.expectedMemory.SetMemory(addr2, val2) e.expectedMemory.SetMemory(addr2, val2)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
...@@ -166,7 +172,7 @@ func (e *ExpectedMTState) PrestateActiveThread() *ExpectedThreadState { ...@@ -166,7 +172,7 @@ func (e *ExpectedMTState) PrestateActiveThread() *ExpectedThreadState {
return e.threadExpectations[e.prestateActiveThreadId] return e.threadExpectations[e.prestateActiveThreadId]
} }
func (e *ExpectedMTState) Thread(threadId uint32) *ExpectedThreadState { func (e *ExpectedMTState) Thread(threadId arch.Word) *ExpectedThreadState {
return e.threadExpectations[threadId] return e.threadExpectations[threadId]
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
//"github.com/ethereum/go-ethereum/common" //"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
) )
...@@ -45,10 +46,10 @@ func TestValidate_shouldCatchMutations(t *testing.T) { ...@@ -45,10 +46,10 @@ func TestValidate_shouldCatchMutations(t *testing.T) {
{name: "LeftStackSize", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.LeftStackSize += 1 }}, {name: "LeftStackSize", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.LeftStackSize += 1 }},
{name: "ActiveThreadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.ActiveThreadId += 1 }}, {name: "ActiveThreadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { e.ActiveThreadId += 1 }},
{name: "Empty thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Empty thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations = map[uint32]*ExpectedThreadState{} e.threadExpectations = map[arch.Word]*ExpectedThreadState{}
}}, }},
{name: "Mismatched thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Mismatched thread expectations", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations = map[uint32]*ExpectedThreadState{someThread.ThreadId: newExpectedThreadState(someThread)} e.threadExpectations = map[arch.Word]*ExpectedThreadState{someThread.ThreadId: newExpectedThreadState(someThread)}
}}, }},
{name: "Active threadId", mut: func(e *ExpectedMTState, st *multithreaded.State) { {name: "Active threadId", mut: func(e *ExpectedMTState, st *multithreaded.State) {
e.threadExpectations[st.GetCurrentThread().ThreadId].ThreadId += 1 e.threadExpectations[st.GetCurrentThread().ThreadId].ThreadId += 1
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
...@@ -27,18 +28,18 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) { ...@@ -27,18 +28,18 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) {
step := r.RandStep() step := r.RandStep()
m.state.PreimageKey = r.RandHash() m.state.PreimageKey = r.RandHash()
m.state.PreimageOffset = r.Uint32() m.state.PreimageOffset = r.Word()
m.state.Step = step m.state.Step = step
m.state.LastHint = r.RandHint() m.state.LastHint = r.RandHint()
m.state.StepsSinceLastContextSwitch = uint64(r.Intn(exec.SchedQuantum)) m.state.StepsSinceLastContextSwitch = uint64(r.Intn(exec.SchedQuantum))
// Randomize memory-related fields // Randomize memory-related fields
halfMemory := math.MaxUint32 / 2 halfMemory := math.MaxUint32 / 2
m.state.Heap = uint32(r.Intn(halfMemory) + halfMemory) m.state.Heap = arch.Word(r.Intn(halfMemory) + halfMemory)
m.state.LLReservationActive = r.Intn(2) == 1 m.state.LLReservationActive = r.Intn(2) == 1
if m.state.LLReservationActive { if m.state.LLReservationActive {
m.state.LLAddress = uint32(r.Intn(halfMemory)) m.state.LLAddress = arch.Word(r.Intn(halfMemory))
m.state.LLOwnerThread = uint32(r.Intn(10)) m.state.LLOwnerThread = arch.Word(r.Intn(10))
} }
// Randomize threads // Randomize threads
...@@ -48,11 +49,11 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) { ...@@ -48,11 +49,11 @@ func (m *StateMutatorMultiThreaded) Randomize(randSeed int64) {
SetupThreads(randSeed+1, m.state, traverseRight, activeStackThreads, inactiveStackThreads) SetupThreads(randSeed+1, m.state, traverseRight, activeStackThreads, inactiveStackThreads)
} }
func (m *StateMutatorMultiThreaded) SetHI(val uint32) { func (m *StateMutatorMultiThreaded) SetHI(val arch.Word) {
m.state.GetCurrentThread().Cpu.HI = val m.state.GetCurrentThread().Cpu.HI = val
} }
func (m *StateMutatorMultiThreaded) SetLO(val uint32) { func (m *StateMutatorMultiThreaded) SetLO(val arch.Word) {
m.state.GetCurrentThread().Cpu.LO = val m.state.GetCurrentThread().Cpu.LO = val
} }
...@@ -64,16 +65,16 @@ func (m *StateMutatorMultiThreaded) SetExited(val bool) { ...@@ -64,16 +65,16 @@ func (m *StateMutatorMultiThreaded) SetExited(val bool) {
m.state.Exited = val m.state.Exited = val
} }
func (m *StateMutatorMultiThreaded) SetPC(val uint32) { func (m *StateMutatorMultiThreaded) SetPC(val arch.Word) {
thread := m.state.GetCurrentThread() thread := m.state.GetCurrentThread()
thread.Cpu.PC = val thread.Cpu.PC = val
} }
func (m *StateMutatorMultiThreaded) SetHeap(val uint32) { func (m *StateMutatorMultiThreaded) SetHeap(val arch.Word) {
m.state.Heap = val m.state.Heap = val
} }
func (m *StateMutatorMultiThreaded) SetNextPC(val uint32) { func (m *StateMutatorMultiThreaded) SetNextPC(val arch.Word) {
thread := m.state.GetCurrentThread() thread := m.state.GetCurrentThread()
thread.Cpu.NextPC = val thread.Cpu.NextPC = val
} }
...@@ -86,7 +87,7 @@ func (m *StateMutatorMultiThreaded) SetPreimageKey(val common.Hash) { ...@@ -86,7 +87,7 @@ func (m *StateMutatorMultiThreaded) SetPreimageKey(val common.Hash) {
m.state.PreimageKey = val m.state.PreimageKey = val
} }
func (m *StateMutatorMultiThreaded) SetPreimageOffset(val uint32) { func (m *StateMutatorMultiThreaded) SetPreimageOffset(val arch.Word) {
m.state.PreimageOffset = val m.state.PreimageOffset = val
} }
......
package testutil package testutil
import ( import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -14,8 +15,8 @@ func RandomThread(randSeed int64) *multithreaded.ThreadState { ...@@ -14,8 +15,8 @@ func RandomThread(randSeed int64) *multithreaded.ThreadState {
thread.Registers = *r.RandRegisters() thread.Registers = *r.RandRegisters()
thread.Cpu.PC = pc thread.Cpu.PC = pc
thread.Cpu.NextPC = pc + 4 thread.Cpu.NextPC = pc + 4
thread.Cpu.HI = r.Uint32() thread.Cpu.HI = r.Word()
thread.Cpu.LO = r.Uint32() thread.Cpu.LO = r.Word()
return thread return thread
} }
...@@ -37,7 +38,7 @@ func InitializeSingleThread(randSeed int, state *multithreaded.State, traverseRi ...@@ -37,7 +38,7 @@ func InitializeSingleThread(randSeed int, state *multithreaded.State, traverseRi
func SetupThreads(randomSeed int64, state *multithreaded.State, traverseRight bool, activeStackSize, otherStackSize int) { func SetupThreads(randomSeed int64, state *multithreaded.State, traverseRight bool, activeStackSize, otherStackSize int) {
var activeStack, otherStack []*multithreaded.ThreadState var activeStack, otherStack []*multithreaded.ThreadState
tid := uint32(0) tid := arch.Word(0)
for i := 0; i < activeStackSize; i++ { for i := 0; i < activeStackSize; i++ {
thread := RandomThread(randomSeed + int64(i)) thread := RandomThread(randomSeed + int64(i))
thread.ThreadId = tid thread.ThreadId = tid
...@@ -129,13 +130,13 @@ func FindNextThreadFiltered(state *multithreaded.State, filter ThreadFilter) *mu ...@@ -129,13 +130,13 @@ func FindNextThreadFiltered(state *multithreaded.State, filter ThreadFilter) *mu
return nil return nil
} }
func FindNextThreadExcluding(state *multithreaded.State, threadId uint32) *multithreaded.ThreadState { func FindNextThreadExcluding(state *multithreaded.State, threadId arch.Word) *multithreaded.ThreadState {
return FindNextThreadFiltered(state, func(t *multithreaded.ThreadState) bool { return FindNextThreadFiltered(state, func(t *multithreaded.ThreadState) bool {
return t.ThreadId != threadId return t.ThreadId != threadId
}) })
} }
func FindThread(state *multithreaded.State, threadId uint32) *multithreaded.ThreadState { func FindThread(state *multithreaded.State, threadId arch.Word) *multithreaded.ThreadState {
for _, t := range GetAllThreads(state) { for _, t := range GetAllThreads(state) {
if t.ThreadId == threadId { if t.ThreadId == threadId {
return t return t
......
...@@ -8,34 +8,47 @@ import ( ...@@ -8,34 +8,47 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
) )
// SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object const (
const SERIALIZED_THREAD_SIZE = 166 THREAD_ID_STATE_WITNESS_OFFSET = 0
THREAD_EXIT_CODE_WITNESS_OFFSET = THREAD_ID_STATE_WITNESS_OFFSET + arch.WordSizeBytes
// THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes. THREAD_EXITED_WITNESS_OFFSET = THREAD_EXIT_CODE_WITNESS_OFFSET + 1
// THREAD_FUTEX_ADDR_WITNESS_OFFSET = THREAD_EXITED_WITNESS_OFFSET + 1
// It consists of the active thread serialized and concatenated with the THREAD_FUTEX_VAL_WITNESS_OFFSET = THREAD_FUTEX_ADDR_WITNESS_OFFSET + arch.WordSizeBytes
// 32 byte hash onion of the active thread stack without the active thread THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET = THREAD_FUTEX_VAL_WITNESS_OFFSET + arch.WordSizeBytes
const THREAD_WITNESS_SIZE = SERIALIZED_THREAD_SIZE + 32 THREAD_FUTEX_CPU_WITNESS_OFFSET = THREAD_FUTEX_TIMEOUT_STEP_WITNESS_OFFSET + 8
THREAD_REGISTERS_WITNESS_OFFSET = THREAD_FUTEX_CPU_WITNESS_OFFSET + (4 * arch.WordSizeBytes)
// SERIALIZED_THREAD_SIZE is the size of a serialized ThreadState object
// 166 and 322 bytes for 32 and 64-bit respectively
SERIALIZED_THREAD_SIZE = THREAD_REGISTERS_WITNESS_OFFSET + (32 * arch.WordSizeBytes)
// THREAD_WITNESS_SIZE is the size of a thread witness encoded in bytes.
//
// It consists of the active thread serialized and concatenated with the
// 32 byte hash onion of the active thread stack without the active thread
THREAD_WITNESS_SIZE = SERIALIZED_THREAD_SIZE + 32
)
// The empty thread root - keccak256(bytes32(0) ++ bytes32(0)) // The empty thread root - keccak256(bytes32(0) ++ bytes32(0))
var EmptyThreadsRoot common.Hash = common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5") var EmptyThreadsRoot common.Hash = common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
type ThreadState struct { type ThreadState struct {
ThreadId uint32 `json:"threadId"` ThreadId Word `json:"threadId"`
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
FutexAddr uint32 `json:"futexAddr"` FutexAddr Word `json:"futexAddr"`
FutexVal uint32 `json:"futexVal"` FutexVal Word `json:"futexVal"`
FutexTimeoutStep uint64 `json:"futexTimeoutStep"` FutexTimeoutStep uint64 `json:"futexTimeoutStep"`
Cpu mipsevm.CpuScalars `json:"cpu"` Cpu mipsevm.CpuScalars `json:"cpu"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `json:"registers"`
} }
func CreateEmptyThread() *ThreadState { func CreateEmptyThread() *ThreadState {
initThreadId := uint32(0) initThreadId := Word(0)
return &ThreadState{ return &ThreadState{
ThreadId: initThreadId, ThreadId: initThreadId,
ExitCode: 0, ExitCode: 0,
...@@ -49,27 +62,27 @@ func CreateEmptyThread() *ThreadState { ...@@ -49,27 +62,27 @@ func CreateEmptyThread() *ThreadState {
FutexAddr: exec.FutexEmptyAddr, FutexAddr: exec.FutexEmptyAddr,
FutexVal: 0, FutexVal: 0,
FutexTimeoutStep: 0, FutexTimeoutStep: 0,
Registers: [32]uint32{}, Registers: [32]Word{},
} }
} }
func (t *ThreadState) serializeThread() []byte { func (t *ThreadState) serializeThread() []byte {
out := make([]byte, 0, SERIALIZED_THREAD_SIZE) out := make([]byte, 0, SERIALIZED_THREAD_SIZE)
out = binary.BigEndian.AppendUint32(out, t.ThreadId) out = arch.ByteOrderWord.AppendWord(out, t.ThreadId)
out = append(out, t.ExitCode) out = append(out, t.ExitCode)
out = mipsevm.AppendBoolToWitness(out, t.Exited) out = mipsevm.AppendBoolToWitness(out, t.Exited)
out = binary.BigEndian.AppendUint32(out, t.FutexAddr) out = arch.ByteOrderWord.AppendWord(out, t.FutexAddr)
out = binary.BigEndian.AppendUint32(out, t.FutexVal) out = arch.ByteOrderWord.AppendWord(out, t.FutexVal)
out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep) out = binary.BigEndian.AppendUint64(out, t.FutexTimeoutStep)
out = binary.BigEndian.AppendUint32(out, t.Cpu.PC) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.PC)
out = binary.BigEndian.AppendUint32(out, t.Cpu.NextPC) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.NextPC)
out = binary.BigEndian.AppendUint32(out, t.Cpu.LO) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.LO)
out = binary.BigEndian.AppendUint32(out, t.Cpu.HI) out = arch.ByteOrderWord.AppendWord(out, t.Cpu.HI)
for _, r := range t.Registers { for _, r := range t.Registers {
out = binary.BigEndian.AppendUint32(out, r) out = arch.ByteOrderWord.AppendWord(out, r)
} }
return out return out
...@@ -115,7 +128,7 @@ func (t *ThreadState) Deserialize(in io.Reader) error { ...@@ -115,7 +128,7 @@ func (t *ThreadState) Deserialize(in io.Reader) error {
if err := binary.Read(in, binary.BigEndian, &t.Cpu.HI); err != nil { if err := binary.Read(in, binary.BigEndian, &t.Cpu.HI); err != nil {
return err return err
} }
// Read the registers as big endian uint32s // Read the registers as big endian Words
for i := range t.Registers { for i := range t.Registers {
if err := binary.Read(in, binary.BigEndian, &t.Registers[i]); err != nil { if err := binary.Read(in, binary.BigEndian, &t.Registers[i]); err != nil {
return err return err
......
...@@ -7,19 +7,22 @@ import ( ...@@ -7,19 +7,22 @@ import (
"io" "io"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
) )
const ( const (
HEAP_START = 0x05_00_00_00 HEAP_START = arch.HeapStart
HEAP_END = 0x60_00_00_00 HEAP_END = arch.HeapEnd
PROGRAM_BREAK = 0x40_00_00_00 PROGRAM_BREAK = arch.ProgramBreak
) )
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T type Word = arch.Word
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart Word) T
func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[T]) (T, error) { func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[T]) (T, error) {
var empty T var empty T
s := initState(uint32(f.Entry), HEAP_START) s := initState(Word(f.Entry), HEAP_START)
for i, prog := range f.Progs { for i, prog := range f.Progs {
if prog.Type == 0x70000003 { // MIPS_ABIFLAGS if prog.Type == 0x70000003 { // MIPS_ABIFLAGS
...@@ -39,13 +42,14 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[ ...@@ -39,13 +42,14 @@ func LoadELF[T mipsevm.FPVMState](f *elf.File, initState CreateInitialFPVMState[
} }
} }
// TODO(#12205)
if prog.Vaddr+prog.Memsz >= uint64(1<<32) { if prog.Vaddr+prog.Memsz >= uint64(1<<32) {
return empty, fmt.Errorf("program %d out of 32-bit mem range: %x - %x (size: %x)", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) return empty, fmt.Errorf("program %d out of 32-bit mem range: %x - %x (size: %x)", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz)
} }
if prog.Vaddr+prog.Memsz >= HEAP_START { if prog.Vaddr+prog.Memsz >= HEAP_START {
return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz) return empty, fmt.Errorf("program %d overlaps with heap: %x - %x (size: %x). The heap start offset must be reconfigured", i, prog.Vaddr, prog.Vaddr+prog.Memsz, prog.Memsz)
} }
if err := s.GetMemory().SetMemoryRange(uint32(prog.Vaddr), r); err != nil { if err := s.GetMemory().SetMemoryRange(Word(prog.Vaddr), r); err != nil {
return empty, fmt.Errorf("failed to read program segment %d: %w", i, err) return empty, fmt.Errorf("failed to read program segment %d: %w", i, err)
} }
} }
......
...@@ -10,8 +10,8 @@ import ( ...@@ -10,8 +10,8 @@ import (
type Symbol struct { type Symbol struct {
Name string `json:"name"` Name string `json:"name"`
Start uint32 `json:"start"` Start Word `json:"start"`
Size uint32 `json:"size"` Size Word `json:"size"`
} }
type Metadata struct { type Metadata struct {
...@@ -31,12 +31,12 @@ func MakeMetadata(elfProgram *elf.File) (*Metadata, error) { ...@@ -31,12 +31,12 @@ func MakeMetadata(elfProgram *elf.File) (*Metadata, error) {
}) })
out := &Metadata{Symbols: make([]Symbol, len(syms))} out := &Metadata{Symbols: make([]Symbol, len(syms))}
for i, s := range syms { for i, s := range syms {
out.Symbols[i] = Symbol{Name: s.Name, Start: uint32(s.Value), Size: uint32(s.Size)} out.Symbols[i] = Symbol{Name: s.Name, Start: Word(s.Value), Size: Word(s.Size)}
} }
return out, nil return out, nil
} }
func (m *Metadata) LookupSymbol(addr uint32) string { func (m *Metadata) LookupSymbol(addr Word) string {
if len(m.Symbols) == 0 { if len(m.Symbols) == 0 {
return "!unknown" return "!unknown"
} }
...@@ -59,12 +59,12 @@ func (m *Metadata) CreateSymbolMatcher(name string) mipsevm.SymbolMatcher { ...@@ -59,12 +59,12 @@ func (m *Metadata) CreateSymbolMatcher(name string) mipsevm.SymbolMatcher {
if s.Name == name { if s.Name == name {
start := s.Start start := s.Start
end := s.Start + s.Size end := s.Start + s.Size
return func(addr uint32) bool { return func(addr Word) bool {
return addr >= start && addr < end return addr >= start && addr < end
} }
} }
} }
return func(addr uint32) bool { return func(addr Word) bool {
return false return false
} }
} }
...@@ -3,14 +3,16 @@ package program ...@@ -3,14 +3,16 @@ package program
import ( import (
"bytes" "bytes"
"debug/elf" "debug/elf"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
const WordSizeBytes = arch.WordSizeBytes
// PatchGoGC patches out garbage-collection-related symbols to disable garbage collection // PatchGoGC patches out garbage-collection-related symbols to disable garbage collection
// and improves performance by patching out floating-point-related symbols // and improves performance by patching out floating-point-related symbols
func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
...@@ -39,10 +41,10 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { ...@@ -39,10 +41,10 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
"flag.init", "flag.init",
// We need to patch this out, we don't pass float64nan because we don't support floats // We need to patch this out, we don't pass float64nan because we don't support floats
"runtime.check": "runtime.check":
// MIPS32 patch: ret (pseudo instruction) // MIPSx patch: ret (pseudo instruction)
// 03e00008 = jr $ra = ret (pseudo instruction) // 03e00008 = jr $ra = ret (pseudo instruction)
// 00000000 = nop (executes with delay-slot, but does nothing) // 00000000 = nop (executes with delay-slot, but does nothing)
if err := st.GetMemory().SetMemoryRange(uint32(s.Value), bytes.NewReader([]byte{ if err := st.GetMemory().SetMemoryRange(Word(s.Value), bytes.NewReader([]byte{
0x03, 0xe0, 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08,
0, 0, 0, 0, 0, 0, 0, 0,
})); err != nil { })); err != nil {
...@@ -56,41 +58,54 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error { ...@@ -56,41 +58,54 @@ func PatchGoGC(f *elf.File, st mipsevm.FPVMState) error {
// PatchStack sets up the program's initial stack frame and stack pointer // PatchStack sets up the program's initial stack frame and stack pointer
func PatchStack(st mipsevm.FPVMState) error { func PatchStack(st mipsevm.FPVMState) error {
// setup stack pointer // setup stack pointer
sp := uint32(0x7f_ff_d0_00) sp := Word(arch.HighMemoryStart)
// allocate 1 page for the initial stack data, and 16KB = 4 pages for the stack to grow // allocate 1 page for the initial stack data, and 16KB = 4 pages for the stack to grow
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 errors.New("failed to allocate page for stack content") return errors.New("failed to allocate page for stack content")
} }
st.GetRegistersRef()[29] = sp st.GetRegistersRef()[29] = sp
storeMem := func(addr uint32, v uint32) { storeMem := func(addr Word, v Word) {
var dat [4]byte var dat [WordSizeBytes]byte
binary.BigEndian.PutUint32(dat[:], v) arch.ByteOrderWord.PutWord(dat[:], v)
_ = st.GetMemory().SetMemoryRange(addr, bytes.NewReader(dat[:])) _ = st.GetMemory().SetMemoryRange(addr, bytes.NewReader(dat[:]))
} }
// init argc, argv, aux on stack auxv3Offset := sp + WordSizeBytes*10
storeMem(sp+4*0, 1) // argc = 1 (argument count) randomness := []byte("4;byfairdiceroll")
storeMem(sp+4*1, sp+4*21) // argv[0] randomness = pad(randomness)
storeMem(sp+4*2, 0) // argv[1] = terminating _ = st.GetMemory().SetMemoryRange(auxv3Offset, bytes.NewReader(randomness))
storeMem(sp+4*3, sp+4*14) // envp[0] = x (offset to first env var)
storeMem(sp+4*4, 0) // envp[1] = terminating
storeMem(sp+4*5, 6) // auxv[0] = _AT_PAGESZ = 6 (key)
storeMem(sp+4*6, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize)
storeMem(sp+4*7, 25) // auxv[2] = AT_RANDOM
storeMem(sp+4*8, sp+4*10) // auxv[3] = address of 16 bytes containing random value
storeMem(sp+4*9, 0) // auxv[term] = 0
_ = st.GetMemory().SetMemoryRange(sp+4*10, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness" envp0Offset := auxv3Offset + Word(len(randomness))
envar := append([]byte("GODEBUG=memprofilerate=0"), 0x0)
envar = pad(envar)
_ = st.GetMemory().SetMemoryRange(envp0Offset, bytes.NewReader(envar))
// append 4 extra zero bytes to end at 4-byte alignment argv0Offset := envp0Offset + Word(len(envar))
envar := append([]byte("GODEBUG=memprofilerate=0"), 0x0, 0x0, 0x0, 0x0) programName := append([]byte("op-program"), 0x0)
_ = st.GetMemory().SetMemoryRange(sp+4*14, bytes.NewReader(envar)) programName = pad(programName)
_ = st.GetMemory().SetMemoryRange(argv0Offset, bytes.NewReader(programName))
// 24 bytes for GODEBUG=memprofilerate=0 + 4 null bytes // init argc, argv, aux on stack
// Then append program name + 2 null bytes for 4-byte alignment storeMem(sp+WordSizeBytes*0, 1) // argc = 1 (argument count)
programName := append([]byte("op-program"), 0x0, 0x0) storeMem(sp+WordSizeBytes*1, argv0Offset) // argv[0]
_ = st.GetMemory().SetMemoryRange(sp+4*21, bytes.NewReader(programName)) storeMem(sp+WordSizeBytes*2, 0) // argv[1] = terminating
storeMem(sp+WordSizeBytes*3, envp0Offset) // envp[0] = x (offset to first env var)
storeMem(sp+WordSizeBytes*4, 0) // envp[1] = terminating
storeMem(sp+WordSizeBytes*5, 6) // auxv[0] = _AT_PAGESZ = 6 (key)
storeMem(sp+WordSizeBytes*6, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize)
storeMem(sp+WordSizeBytes*7, 25) // auxv[2] = AT_RANDOM
storeMem(sp+WordSizeBytes*8, auxv3Offset) // auxv[3] = address of 16 bytes containing random value
storeMem(sp+WordSizeBytes*9, 0) // auxv[term] = 0
return nil return nil
} }
// pad adds appropriate padding to buf to end at Word alignment
func pad(buf []byte) []byte {
if len(buf)%WordSizeBytes == 0 {
return buf
}
bytesToAlignment := WordSizeBytes - len(buf)%WordSizeBytes
return append(buf, make([]byte, bytesToAlignment)...)
}
...@@ -28,7 +28,7 @@ var _ mipsevm.FPVM = (*InstrumentedState)(nil) ...@@ -28,7 +28,7 @@ var _ mipsevm.FPVM = (*InstrumentedState)(nil)
func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) *InstrumentedState { func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, meta mipsevm.Metadata) *InstrumentedState {
var sleepCheck mipsevm.SymbolMatcher var sleepCheck mipsevm.SymbolMatcher
if meta == nil { if meta == nil {
sleepCheck = func(addr uint32) bool { return false } sleepCheck = func(addr Word) bool { return false }
} else { } else {
sleepCheck = meta.CreateSymbolMatcher("runtime.notesleep") sleepCheck = meta.CreateSymbolMatcher("runtime.notesleep")
} }
...@@ -75,7 +75,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro ...@@ -75,7 +75,7 @@ func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err erro
memProof := m.memoryTracker.MemProof() memProof := m.memoryTracker.MemProof()
wit.ProofData = append(wit.ProofData, memProof[:]...) wit.ProofData = append(wit.ProofData, memProof[:]...)
lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage()
if lastPreimageOffset != ^uint32(0) { if lastPreimageOffset != ^Word(0) {
wit.PreimageOffset = lastPreimageOffset wit.PreimageOffset = lastPreimageOffset
wit.PreimageKey = lastPreimageKey wit.PreimageKey = lastPreimageKey
wit.PreimageValue = lastPreimage wit.PreimageValue = lastPreimage
...@@ -88,7 +88,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool { ...@@ -88,7 +88,7 @@ func (m *InstrumentedState) CheckInfiniteLoop() bool {
return m.sleepCheck(m.state.GetPC()) return m.sleepCheck(m.state.GetPC())
} }
func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, uint32) { func (m *InstrumentedState) LastPreimage() ([32]byte, []byte, Word) {
return m.preimageOracle.LastPreimage() return m.preimageOracle.LastPreimage()
} }
...@@ -109,7 +109,7 @@ func (m *InstrumentedState) Traceback() { ...@@ -109,7 +109,7 @@ func (m *InstrumentedState) Traceback() {
m.stackTracker.Traceback() m.stackTracker.Traceback()
} }
func (m *InstrumentedState) LookupSymbol(addr uint32) string { func (m *InstrumentedState) LookupSymbol(addr Word) string {
if m.meta == nil { if m.meta == nil {
return "" return ""
} }
......
...@@ -6,24 +6,26 @@ import ( ...@@ -6,24 +6,26 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
type Word = arch.Word
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
syscallNum, a0, a1, a2, _ := exec.GetSyscallArgs(&m.state.Registers) syscallNum, a0, a1, a2, _ := exec.GetSyscallArgs(&m.state.Registers)
v0 := uint32(0) v0 := Word(0)
v1 := uint32(0) v1 := Word(0)
//fmt.Printf("syscall: %d\n", syscallNum) //fmt.Printf("syscall: %d\n", syscallNum)
switch syscallNum { switch syscallNum {
case exec.SysMmap: case exec.SysMmap:
var newHeap uint32 var newHeap Word
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = program.PROGRAM_BREAK v0 = arch.ProgramBreak
case exec.SysClone: // clone (not supported) case exec.SysClone: // clone (not supported)
v0 = 1 v0 = 1
case exec.SysExitGroup: case exec.SysExitGroup:
...@@ -31,13 +33,13 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -31,13 +33,13 @@ func (m *InstrumentedState) handleSyscall() error {
m.state.ExitCode = uint8(a0) m.state.ExitCode = uint8(a0)
return nil return nil
case exec.SysRead: case exec.SysRead:
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newPreimageOffset, _, _ = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker) v0, v1, newPreimageOffset, _, _ = exec.HandleSysRead(a0, a1, a2, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker)
m.state.PreimageOffset = newPreimageOffset m.state.PreimageOffset = newPreimageOffset
case exec.SysWrite: case exec.SysWrite:
var newLastHint hexutil.Bytes var newLastHint hexutil.Bytes
var newPreimageKey common.Hash var newPreimageKey common.Hash
var newPreimageOffset uint32 var newPreimageOffset Word
v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr) v0, v1, newLastHint, newPreimageKey, newPreimageOffset = exec.HandleSysWrite(a0, a1, a2, m.state.LastHint, m.state.PreimageKey, m.state.PreimageOffset, m.preimageOracle, m.state.Memory, m.memoryTracker, m.stdOut, m.stdErr)
m.state.LastHint = newLastHint m.state.LastHint = newLastHint
m.state.PreimageKey = newPreimageKey m.state.PreimageKey = newPreimageKey
...@@ -78,19 +80,19 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -78,19 +80,19 @@ func (m *InstrumentedState) mipsStep() error {
func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error { func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error {
baseReg := (insn >> 21) & 0x1F baseReg := (insn >> 21) & 0x1F
base := m.state.Registers[baseReg] base := m.state.Registers[baseReg]
rtReg := (insn >> 16) & 0x1F rtReg := Word((insn >> 16) & 0x1F)
offset := exec.SignExtendImmediate(insn) offset := exec.SignExtendImmediate(insn)
effAddr := (base + offset) & 0xFFFFFFFC effAddr := (base + offset) & arch.AddressMask
m.memoryTracker.TrackMemAccess(effAddr) m.memoryTracker.TrackMemAccess(effAddr)
mem := m.state.Memory.GetMemory(effAddr) mem := m.state.Memory.GetWord(effAddr)
var retVal uint32 var retVal Word
if opcode == exec.OpLoadLinked { if opcode == exec.OpLoadLinked {
retVal = mem retVal = mem
} else if opcode == exec.OpStoreConditional { } else if opcode == exec.OpStoreConditional {
rt := m.state.Registers[rtReg] rt := m.state.Registers[rtReg]
m.state.Memory.SetMemory(effAddr, rt) m.state.Memory.SetWord(effAddr, rt)
retVal = 1 // 1 for success retVal = 1 // 1 for success
} else { } else {
panic(fmt.Sprintf("Invalid instruction passed to handleRMWOps (opcode %08x)", opcode)) panic(fmt.Sprintf("Invalid instruction passed to handleRMWOps (opcode %08x)", opcode))
......
...@@ -13,28 +13,30 @@ import ( ...@@ -13,28 +13,30 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
// STATE_WITNESS_SIZE is the size of the state witness encoding in bytes. // STATE_WITNESS_SIZE is the size of the state witness encoding in bytes.
// ignoring 64-bit STATE_WITNESS_SIZE as it's not supported for singlethreaded
const STATE_WITNESS_SIZE = 226 const STATE_WITNESS_SIZE = 226
type State struct { type State struct {
Memory *memory.Memory `json:"memory"` Memory *memory.Memory `json:"memory"`
PreimageKey common.Hash `json:"preimageKey"` PreimageKey common.Hash `json:"preimageKey"`
PreimageOffset uint32 `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix PreimageOffset Word `json:"preimageOffset"` // note that the offset includes the 8-byte length prefix
Cpu mipsevm.CpuScalars `json:"cpu"` Cpu mipsevm.CpuScalars `json:"cpu"`
Heap uint32 `json:"heap"` // to handle mmap growth Heap Word `json:"heap"` // to handle mmap growth
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
Step uint64 `json:"step"` Step uint64 `json:"step"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `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.
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
...@@ -51,7 +53,7 @@ func CreateEmptyState() *State { ...@@ -51,7 +53,7 @@ func CreateEmptyState() *State {
HI: 0, HI: 0,
}, },
Heap: 0, Heap: 0,
Registers: [32]uint32{}, Registers: [32]Word{},
Memory: memory.NewMemory(), Memory: memory.NewMemory(),
ExitCode: 0, ExitCode: 0,
Exited: false, Exited: false,
...@@ -59,7 +61,7 @@ func CreateEmptyState() *State { ...@@ -59,7 +61,7 @@ func CreateEmptyState() *State {
} }
} }
func CreateInitialState(pc, heapStart uint32) *State { func CreateInitialState(pc, heapStart Word) *State {
state := CreateEmptyState() state := CreateEmptyState()
state.Cpu.PC = pc state.Cpu.PC = pc
state.Cpu.NextPC = pc + 4 state.Cpu.NextPC = pc + 4
...@@ -75,16 +77,16 @@ func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, s ...@@ -75,16 +77,16 @@ func (s *State) CreateVM(logger log.Logger, po mipsevm.PreimageOracle, stdOut, s
type stateMarshaling struct { type stateMarshaling struct {
Memory *memory.Memory `json:"memory"` Memory *memory.Memory `json:"memory"`
PreimageKey common.Hash `json:"preimageKey"` PreimageKey common.Hash `json:"preimageKey"`
PreimageOffset uint32 `json:"preimageOffset"` PreimageOffset Word `json:"preimageOffset"`
PC uint32 `json:"pc"` PC Word `json:"pc"`
NextPC uint32 `json:"nextPC"` NextPC Word `json:"nextPC"`
LO uint32 `json:"lo"` LO Word `json:"lo"`
HI uint32 `json:"hi"` HI Word `json:"hi"`
Heap uint32 `json:"heap"` Heap Word `json:"heap"`
ExitCode uint8 `json:"exit"` ExitCode uint8 `json:"exit"`
Exited bool `json:"exited"` Exited bool `json:"exited"`
Step uint64 `json:"step"` Step uint64 `json:"step"`
Registers [32]uint32 `json:"registers"` Registers [32]Word `json:"registers"`
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
} }
...@@ -128,11 +130,11 @@ func (s *State) UnmarshalJSON(data []byte) error { ...@@ -128,11 +130,11 @@ func (s *State) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func (s *State) GetPC() uint32 { return s.Cpu.PC } func (s *State) GetPC() Word { return s.Cpu.PC }
func (s *State) GetCpu() mipsevm.CpuScalars { return s.Cpu } func (s *State) GetCpu() mipsevm.CpuScalars { return s.Cpu }
func (s *State) GetRegistersRef() *[32]uint32 { return &s.Registers } func (s *State) GetRegistersRef() *[32]Word { return &s.Registers }
func (s *State) GetExitCode() uint8 { return s.ExitCode } func (s *State) GetExitCode() uint8 { return s.ExitCode }
...@@ -152,7 +154,7 @@ func (s *State) GetMemory() *memory.Memory { ...@@ -152,7 +154,7 @@ func (s *State) GetMemory() *memory.Memory {
return s.Memory return s.Memory
} }
func (s *State) GetHeap() uint32 { func (s *State) GetHeap() Word {
return s.Heap return s.Heap
} }
...@@ -160,7 +162,7 @@ func (s *State) GetPreimageKey() common.Hash { ...@@ -160,7 +162,7 @@ func (s *State) GetPreimageKey() common.Hash {
return s.PreimageKey return s.PreimageKey
} }
func (s *State) GetPreimageOffset() uint32 { func (s *State) GetPreimageOffset() Word {
return s.PreimageOffset return s.PreimageOffset
} }
...@@ -169,17 +171,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -169,17 +171,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
memRoot := s.Memory.MerkleRoot() memRoot := s.Memory.MerkleRoot()
out = append(out, memRoot[:]...) out = append(out, memRoot[:]...)
out = append(out, s.PreimageKey[:]...) out = append(out, s.PreimageKey[:]...)
out = binary.BigEndian.AppendUint32(out, s.PreimageOffset) out = arch.ByteOrderWord.AppendWord(out, s.PreimageOffset)
out = binary.BigEndian.AppendUint32(out, s.Cpu.PC) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.PC)
out = binary.BigEndian.AppendUint32(out, s.Cpu.NextPC) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.NextPC)
out = binary.BigEndian.AppendUint32(out, s.Cpu.LO) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.LO)
out = binary.BigEndian.AppendUint32(out, s.Cpu.HI) out = arch.ByteOrderWord.AppendWord(out, s.Cpu.HI)
out = binary.BigEndian.AppendUint32(out, s.Heap) out = arch.ByteOrderWord.AppendWord(out, s.Heap)
out = append(out, s.ExitCode) out = append(out, s.ExitCode)
out = mipsevm.AppendBoolToWitness(out, s.Exited) out = mipsevm.AppendBoolToWitness(out, s.Exited)
out = binary.BigEndian.AppendUint64(out, s.Step) out = binary.BigEndian.AppendUint64(out, s.Step)
for _, r := range s.Registers { for _, r := range s.Registers {
out = binary.BigEndian.AppendUint32(out, r) out = arch.ByteOrderWord.AppendWord(out, r)
} }
return out, stateHashFromWitness(out) return out, stateHashFromWitness(out)
} }
...@@ -191,17 +193,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) { ...@@ -191,17 +193,17 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
// StateVersion uint8(0) // StateVersion uint8(0)
// Memory As per Memory.Serialize // Memory As per Memory.Serialize
// PreimageKey [32]byte // PreimageKey [32]byte
// PreimageOffset uint32 // PreimageOffset Word
// Cpu.PC uint32 // Cpu.PC Word
// Cpu.NextPC uint32 // Cpu.NextPC Word
// Cpu.LO uint32 // Cpu.LO Word
// Cpu.HI uint32 // Cpu.HI Word
// Heap uint32 // Heap Word
// ExitCode uint8 // ExitCode uint8
// Exited uint8 - 0 for false, 1 for true // Exited uint8 - 0 for false, 1 for true
// Step uint64 // Step uint64
// Registers [32]uint32 // Registers [32]Word
// len(LastHint) uint32 (0 when LastHint is nil) // len(LastHint) Word (0 when LastHint is nil)
// LastHint []byte // LastHint []byte
func (s *State) Serialize(out io.Writer) error { func (s *State) Serialize(out io.Writer) error {
bout := serialize.NewBinaryWriter(out) bout := serialize.NewBinaryWriter(out)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,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/arch"
"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"
) )
...@@ -128,7 +129,7 @@ func TestSerializeStateRoundTrip(t *testing.T) { ...@@ -128,7 +129,7 @@ func TestSerializeStateRoundTrip(t *testing.T) {
ExitCode: 1, ExitCode: 1,
Exited: true, Exited: true,
Step: 0xdeadbeef, Step: 0xdeadbeef,
Registers: [32]uint32{ Registers: [32]arch.Word{
0xdeadbeef, 0xdeadbeef,
0xdeadbeef, 0xdeadbeef,
0xc0ffee, 0xc0ffee,
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -19,12 +20,12 @@ func (m *StateMutatorSingleThreaded) Randomize(randSeed int64) { ...@@ -19,12 +20,12 @@ func (m *StateMutatorSingleThreaded) Randomize(randSeed int64) {
step := r.RandStep() step := r.RandStep()
m.state.PreimageKey = r.RandHash() m.state.PreimageKey = r.RandHash()
m.state.PreimageOffset = r.Uint32() m.state.PreimageOffset = r.Word()
m.state.Cpu.PC = pc m.state.Cpu.PC = pc
m.state.Cpu.NextPC = pc + 4 m.state.Cpu.NextPC = pc + 4
m.state.Cpu.HI = r.Uint32() m.state.Cpu.HI = r.Word()
m.state.Cpu.LO = r.Uint32() m.state.Cpu.LO = r.Word()
m.state.Heap = r.Uint32() m.state.Heap = r.Word()
m.state.Step = step m.state.Step = step
m.state.LastHint = r.RandHint() m.state.LastHint = r.RandHint()
m.state.Registers = *r.RandRegisters() m.state.Registers = *r.RandRegisters()
...@@ -36,23 +37,23 @@ func NewStateMutatorSingleThreaded(state *singlethreaded.State) testutil.StateMu ...@@ -36,23 +37,23 @@ func NewStateMutatorSingleThreaded(state *singlethreaded.State) testutil.StateMu
return &StateMutatorSingleThreaded{state: state} return &StateMutatorSingleThreaded{state: state}
} }
func (m *StateMutatorSingleThreaded) SetPC(val uint32) { func (m *StateMutatorSingleThreaded) SetPC(val arch.Word) {
m.state.Cpu.PC = val m.state.Cpu.PC = val
} }
func (m *StateMutatorSingleThreaded) SetNextPC(val uint32) { func (m *StateMutatorSingleThreaded) SetNextPC(val arch.Word) {
m.state.Cpu.NextPC = val m.state.Cpu.NextPC = val
} }
func (m *StateMutatorSingleThreaded) SetHI(val uint32) { func (m *StateMutatorSingleThreaded) SetHI(val arch.Word) {
m.state.Cpu.HI = val m.state.Cpu.HI = val
} }
func (m *StateMutatorSingleThreaded) SetLO(val uint32) { func (m *StateMutatorSingleThreaded) SetLO(val arch.Word) {
m.state.Cpu.LO = val m.state.Cpu.LO = val
} }
func (m *StateMutatorSingleThreaded) SetHeap(val uint32) { func (m *StateMutatorSingleThreaded) SetHeap(val arch.Word) {
m.state.Heap = val m.state.Heap = val
} }
...@@ -72,7 +73,7 @@ func (m *StateMutatorSingleThreaded) SetPreimageKey(val common.Hash) { ...@@ -72,7 +73,7 @@ func (m *StateMutatorSingleThreaded) SetPreimageKey(val common.Hash) {
m.state.PreimageKey = val m.state.PreimageKey = val
} }
func (m *StateMutatorSingleThreaded) SetPreimageOffset(val uint32) { func (m *StateMutatorSingleThreaded) SetPreimageOffset(val arch.Word) {
m.state.PreimageOffset = val m.state.PreimageOffset = val
} }
......
package mipsevm package mipsevm
import "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
type CpuScalars struct { type CpuScalars struct {
PC uint32 `json:"pc"` PC arch.Word `json:"pc"`
NextPC uint32 `json:"nextPC"` NextPC arch.Word `json:"nextPC"`
LO uint32 `json:"lo"` LO arch.Word `json:"lo"`
HI uint32 `json:"hi"` HI arch.Word `json:"hi"`
} }
const ( const (
......
This diff is collapsed.
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/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/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
...@@ -20,10 +21,10 @@ func TestEVM_LL(t *testing.T) { ...@@ -20,10 +21,10 @@ func TestEVM_LL(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5},
...@@ -37,12 +38,12 @@ func TestEVM_LL(t *testing.T) { ...@@ -37,12 +38,12 @@ func TestEVM_LL(t *testing.T) {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_0000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4))
state := goVm.GetState() state := goVm.GetState()
state.GetMemory().SetMemory(pc, insn) state.GetMemory().SetMemory(pc, insn)
state.GetMemory().SetMemory(c.effAddr, c.value) state.GetMemory().SetWord(c.effAddr, c.value)
state.GetRegistersRef()[baseReg] = c.base state.GetRegistersRef()[baseReg] = c.base
step := state.GetStep() step := state.GetStep()
...@@ -70,10 +71,10 @@ func TestEVM_SC(t *testing.T) { ...@@ -70,10 +71,10 @@ func TestEVM_SC(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
base uint32 base Word
offset int offset int
value uint32 value Word
effAddr uint32 effAddr Word
rtReg int rtReg int
}{ }{
{name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5}, {name: "Aligned effAddr", base: 0x00_00_00_01, offset: 0x0133, value: 0xABCD, effAddr: 0x00_00_01_34, rtReg: 5},
...@@ -87,7 +88,7 @@ func TestEVM_SC(t *testing.T) { ...@@ -87,7 +88,7 @@ func TestEVM_SC(t *testing.T) {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
rtReg := c.rtReg rtReg := c.rtReg
baseReg := 6 baseReg := 6
pc := uint32(0x44) pc := Word(0x44)
insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset)) insn := uint32((0b11_1000 << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(pc), testutil.WithNextPC(pc+4))
state := goVm.GetState() state := goVm.GetState()
...@@ -103,7 +104,7 @@ func TestEVM_SC(t *testing.T) { ...@@ -103,7 +104,7 @@ func TestEVM_SC(t *testing.T) {
expected.NextPC = pc + 8 expected.NextPC = pc + 8
expectedMemory := memory.NewMemory() expectedMemory := memory.NewMemory()
expectedMemory.SetMemory(pc, insn) expectedMemory.SetMemory(pc, insn)
expectedMemory.SetMemory(c.effAddr, c.value) expectedMemory.SetWord(c.effAddr, c.value)
expected.MemoryRoot = expectedMemory.MerkleRoot() expected.MemoryRoot = expectedMemory.MerkleRoot()
if rtReg != 0 { if rtReg != 0 {
expected.Registers[rtReg] = 1 // 1 for success expected.Registers[rtReg] = 1 // 1 for success
...@@ -130,10 +131,10 @@ func TestEVM_SysRead_Preimage(t *testing.T) { ...@@ -130,10 +131,10 @@ func TestEVM_SysRead_Preimage(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
addr uint32 addr Word
count uint32 count Word
writeLen uint32 writeLen Word
preimageOffset uint32 preimageOffset Word
prestateMem uint32 prestateMem uint32
postateMem uint32 postateMem uint32
shouldPanic bool shouldPanic bool
...@@ -157,7 +158,7 @@ func TestEVM_SysRead_Preimage(t *testing.T) { ...@@ -157,7 +158,7 @@ func TestEVM_SysRead_Preimage(t *testing.T) {
} }
for i, c := range cases { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
effAddr := 0xFFffFFfc & c.addr effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue) oracle := testutil.StaticOracle(t, preimageValue)
goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPreimageKey(preimageKey), testutil.WithPreimageOffset(c.preimageOffset)) goVm := v.VMFactory(oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPreimageKey(preimageKey), testutil.WithPreimageOffset(c.preimageOffset))
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/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"
...@@ -50,13 +51,13 @@ func FuzzStateSyscallBrk(f *testing.F) { ...@@ -50,13 +51,13 @@ func FuzzStateSyscallBrk(f *testing.F) {
func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation // Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1)) f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2))
// Check edge case - just within bounds // Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3)) f.Add(Word(0), Word(0x1000), Word(program.HEAP_END-4096), int64(3))
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
...@@ -112,7 +113,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ...@@ -112,7 +113,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) {
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
state.GetRegistersRef()[2] = exec.SysExitGroup state.GetRegistersRef()[2] = exec.SysExitGroup
state.GetRegistersRef()[4] = uint32(exitCode) state.GetRegistersRef()[4] = Word(exitCode)
state.GetMemory().SetMemory(state.GetPC(), syscallInsn) state.GetMemory().SetMemory(state.GetPC(), syscallInsn)
step := state.GetStep() step := state.GetStep()
...@@ -134,7 +135,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) { ...@@ -134,7 +135,7 @@ func FuzzStateSyscallExitGroup(f *testing.F) {
func FuzzStateSyscallFcntl(f *testing.F) { func FuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd uint32, cmd uint32, seed int64) { f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
...@@ -190,7 +191,7 @@ func FuzzStateSyscallFcntl(f *testing.F) { ...@@ -190,7 +191,7 @@ func FuzzStateSyscallFcntl(f *testing.F) {
func FuzzStateHintRead(f *testing.F) { func FuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, seed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
preimageData := []byte("hello world") preimageData := []byte("hello world")
...@@ -227,15 +228,15 @@ func FuzzStateHintRead(f *testing.F) { ...@@ -227,15 +228,15 @@ func FuzzStateHintRead(f *testing.F) {
func FuzzStatePreimageRead(f *testing.F) { func FuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, pc uint32, count uint32, preimageOffset uint32, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
effAddr := addr & 0xFF_FF_FF_FC effAddr := addr & arch.AddressMask
pc = pc & 0xFF_FF_FF_FC pc = pc & arch.AddressMask
preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF} preexistingMemoryVal := [4]byte{0xFF, 0xFF, 0xFF, 0xFF}
preimageValue := []byte("hello world") preimageValue := []byte("hello world")
preimageData := testutil.AddPreimageLengthPrefix(preimageValue) preimageData := testutil.AddPreimageLengthPrefix(preimageValue)
if preimageOffset >= uint32(len(preimageData)) || pc == effAddr { if preimageOffset >= Word(len(preimageData)) || pc == effAddr {
t.SkipNow() t.SkipNow()
} }
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
...@@ -252,13 +253,13 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -252,13 +253,13 @@ func FuzzStatePreimageRead(f *testing.F) {
state.GetMemory().SetMemory(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:])) state.GetMemory().SetMemory(effAddr, binary.BigEndian.Uint32(preexistingMemoryVal[:]))
step := state.GetStep() step := state.GetStep()
alignment := addr & 3 alignment := addr & arch.ExtMask
writeLen := 4 - alignment writeLen := 4 - alignment
if count < writeLen { if count < writeLen {
writeLen = count writeLen = count
} }
// Cap write length to remaining bytes of the preimage // Cap write length to remaining bytes of the preimage
preimageDataLen := uint32(len(preimageData)) preimageDataLen := Word(len(preimageData))
if preimageOffset+writeLen > preimageDataLen { if preimageOffset+writeLen > preimageDataLen {
writeLen = preimageDataLen - preimageOffset writeLen = preimageDataLen - preimageOffset
} }
...@@ -290,11 +291,11 @@ func FuzzStatePreimageRead(f *testing.F) { ...@@ -290,11 +291,11 @@ func FuzzStatePreimageRead(f *testing.F) {
func FuzzStateHintWrite(f *testing.F) { func FuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, hint1, hint2, hint3 []byte, randSeed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
// Make sure pc does not overlap with hint data in memory // Make sure pc does not overlap with hint data in memory
pc := uint32(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
addr += 8 addr += 8
} }
...@@ -372,15 +373,15 @@ func FuzzStateHintWrite(f *testing.F) { ...@@ -372,15 +373,15 @@ func FuzzStateHintWrite(f *testing.F) {
func FuzzStatePreimageWrite(f *testing.F) { func FuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
// Make sure pc does not overlap with preimage data in memory // Make sure pc does not overlap with preimage data in memory
pc := uint32(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
addr += 8 addr += 8
} }
effAddr := addr & 0xFF_FF_FF_FC effAddr := addr & arch.AddressMask
preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78} preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78}
preimageData := []byte("hello world") preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
...@@ -398,7 +399,7 @@ func FuzzStatePreimageWrite(f *testing.F) { ...@@ -398,7 +399,7 @@ func FuzzStatePreimageWrite(f *testing.F) {
step := state.GetStep() step := state.GetStep()
expectBytesWritten := count expectBytesWritten := count
alignment := addr & 0x3 alignment := addr & arch.ExtMask
sz := 4 - alignment sz := 4 - alignment
if sz < expectBytesWritten { if sz < expectBytesWritten {
expectBytesWritten = sz expectBytesWritten = sz
......
...@@ -14,13 +14,13 @@ import ( ...@@ -14,13 +14,13 @@ import (
func FuzzStateSyscallCloneMT(f *testing.F) { func FuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f) v := GetMultiThreadedTestCase(f)
f.Fuzz(func(t *testing.T, nextThreadId, stackPtr uint32, seed int64) { f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := mttestutil.GetMtState(t, goVm) state := mttestutil.GetMtState(t, goVm)
// Update existing threads to avoid collision with nextThreadId // Update existing threads to avoid collision with nextThreadId
if mttestutil.FindThread(state, nextThreadId) != nil { if mttestutil.FindThread(state, nextThreadId) != nil {
for i, t := range mttestutil.GetAllThreads(state) { for i, t := range mttestutil.GetAllThreads(state) {
t.ThreadId = nextThreadId - uint32(i+1) t.ThreadId = nextThreadId - Word(i+1)
} }
} }
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,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/arch"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
...@@ -97,7 +98,7 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev ...@@ -97,7 +98,7 @@ func EncodeStepInput(t *testing.T, wit *mipsevm.StepWitness, localContext mipsev
return input return input
} }
func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32, localContext mipsevm.LocalContext) ([]byte, error) { func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, localContext mipsevm.LocalContext) ([]byte, error) {
if 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")
} }
...@@ -151,7 +152,7 @@ func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte, ...@@ -151,7 +152,7 @@ func (m *MIPSEVM) encodePreimageOracleInput(t *testing.T, preimageKey [32]byte,
} }
} }
func (m *MIPSEVM) assertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset uint32) { func (m *MIPSEVM) assertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word) {
poInput, err := m.encodePreimageOracleInput(t, preimageKey, preimageValue, preimageOffset, mipsevm.LocalContext{}) poInput, err := m.encodePreimageOracleInput(t, preimageKey, preimageValue, preimageOffset, mipsevm.LocalContext{})
require.NoError(t, err, "encode preimage oracle input") require.NoError(t, err, "encode preimage oracle input")
_, _, evmErr := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560) _, _, evmErr := m.env.Call(m.sender, m.addrs.Oracle, poInput, m.startingGas, common.U2560)
...@@ -200,7 +201,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract ...@@ -200,7 +201,7 @@ func AssertEVMReverts(t *testing.T, state mipsevm.FPVMState, contracts *Contract
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) { func AssertPreimageOracleReverts(t *testing.T, preimageKey [32]byte, preimageValue []byte, preimageOffset arch.Word, contracts *ContractMetadata, tracer *tracing.Hooks) {
evm := NewMIPSEVM(contracts) evm := NewMIPSEVM(contracts)
evm.SetTracer(tracer) evm.SetTracer(tracer)
LogStepFailureAtCleanup(t, evm) LogStepFailureAtCleanup(t, evm)
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"math/rand" "math/rand"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -21,6 +22,14 @@ func (h *RandHelper) Uint32() uint32 { ...@@ -21,6 +22,14 @@ func (h *RandHelper) Uint32() uint32 {
return h.r.Uint32() return h.r.Uint32()
} }
func (h *RandHelper) Word() arch.Word {
if arch.IsMips32 {
return arch.Word(h.r.Uint32())
} else {
return arch.Word(h.r.Uint64())
}
}
func (h *RandHelper) Fraction() float64 { func (h *RandHelper) Fraction() float64 {
return h.r.Float64() return h.r.Float64()
} }
...@@ -57,10 +66,10 @@ func (h *RandHelper) RandHint() []byte { ...@@ -57,10 +66,10 @@ func (h *RandHelper) RandHint() []byte {
return bytes return bytes
} }
func (h *RandHelper) RandRegisters() *[32]uint32 { func (h *RandHelper) RandRegisters() *[32]arch.Word {
registers := new([32]uint32) registers := new([32]arch.Word)
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
registers[i] = h.r.Uint32() registers[i] = h.Word()
} }
return registers return registers
} }
...@@ -73,8 +82,8 @@ func (h *RandHelper) RandomBytes(t require.TestingT, length int) []byte { ...@@ -73,8 +82,8 @@ func (h *RandHelper) RandomBytes(t require.TestingT, length int) []byte {
return randBytes return randBytes
} }
func (h *RandHelper) RandPC() uint32 { func (h *RandHelper) RandPC() arch.Word {
return AlignPC(h.r.Uint32()) return AlignPC(h.Word())
} }
func (h *RandHelper) RandStep() uint64 { func (h *RandHelper) RandStep() uint64 {
......
...@@ -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/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
) )
...@@ -33,12 +34,12 @@ func AddPreimageLengthPrefix(data []byte) []byte { ...@@ -33,12 +34,12 @@ func AddPreimageLengthPrefix(data []byte) []byte {
type StateMutator interface { type StateMutator interface {
SetPreimageKey(val common.Hash) SetPreimageKey(val common.Hash)
SetPreimageOffset(val uint32) SetPreimageOffset(val arch.Word)
SetPC(val uint32) SetPC(val arch.Word)
SetNextPC(val uint32) SetNextPC(val arch.Word)
SetHI(val uint32) SetHI(val arch.Word)
SetLO(val uint32) SetLO(val arch.Word)
SetHeap(addr uint32) SetHeap(addr arch.Word)
SetExitCode(val uint8) SetExitCode(val uint8)
SetExited(val bool) SetExited(val bool)
SetStep(val uint64) SetStep(val uint64)
...@@ -48,26 +49,26 @@ type StateMutator interface { ...@@ -48,26 +49,26 @@ type StateMutator interface {
type StateOption func(state StateMutator) type StateOption func(state StateMutator)
func WithPC(pc uint32) StateOption { func WithPC(pc arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPC(pc) state.SetPC(pc)
} }
} }
func WithNextPC(nextPC uint32) StateOption { func WithNextPC(nextPC arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetNextPC(nextPC) state.SetNextPC(nextPC)
} }
} }
func WithPCAndNextPC(pc uint32) StateOption { func WithPCAndNextPC(pc arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPC(pc) state.SetPC(pc)
state.SetNextPC(pc + 4) state.SetNextPC(pc + 4)
} }
} }
func WithHeap(addr uint32) StateOption { func WithHeap(addr arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetHeap(addr) state.SetHeap(addr)
} }
...@@ -85,7 +86,7 @@ func WithPreimageKey(key common.Hash) StateOption { ...@@ -85,7 +86,7 @@ func WithPreimageKey(key common.Hash) StateOption {
} }
} }
func WithPreimageOffset(offset uint32) StateOption { func WithPreimageOffset(offset arch.Word) StateOption {
return func(state StateMutator) { return func(state StateMutator) {
state.SetPreimageOffset(offset) state.SetPreimageOffset(offset)
} }
...@@ -103,12 +104,12 @@ func WithRandomization(seed int64) StateOption { ...@@ -103,12 +104,12 @@ func WithRandomization(seed int64) StateOption {
} }
} }
func AlignPC(pc uint32) uint32 { func AlignPC(pc arch.Word) arch.Word {
// Memory-align random pc and leave room for nextPC // Memory-align random pc and leave room for nextPC
pc = pc & 0xFF_FF_FF_FC // Align address pc = pc & arch.AddressMask // Align address
if pc >= 0xFF_FF_FF_FC { if pc >= arch.AddressMask && arch.IsMips32 {
// Leave room to set and then increment nextPC // Leave room to set and then increment nextPC
pc = 0xFF_FF_FF_FC - 8 pc = arch.AddressMask - 8
} }
return pc return pc
} }
...@@ -123,17 +124,17 @@ func BoundStep(step uint64) uint64 { ...@@ -123,17 +124,17 @@ func BoundStep(step uint64) uint64 {
type ExpectedState struct { type ExpectedState struct {
PreimageKey common.Hash PreimageKey common.Hash
PreimageOffset uint32 PreimageOffset arch.Word
PC uint32 PC arch.Word
NextPC uint32 NextPC arch.Word
HI uint32 HI arch.Word
LO uint32 LO arch.Word
Heap uint32 Heap arch.Word
ExitCode uint8 ExitCode uint8
Exited bool Exited bool
Step uint64 Step uint64
LastHint hexutil.Bytes LastHint hexutil.Bytes
Registers [32]uint32 Registers [32]arch.Word
MemoryRoot common.Hash MemoryRoot common.Hash
expectedMemory *memory.Memory expectedMemory *memory.Memory
} }
...@@ -164,7 +165,7 @@ func (e *ExpectedState) ExpectStep() { ...@@ -164,7 +165,7 @@ func (e *ExpectedState) ExpectStep() {
e.NextPC += 4 e.NextPC += 4
} }
func (e *ExpectedState) ExpectMemoryWrite(addr uint32, val uint32) { func (e *ExpectedState) ExpectMemoryWrite(addr arch.Word, val uint32) {
e.expectedMemory.SetMemory(addr, val) e.expectedMemory.SetMemory(addr, val)
e.MemoryRoot = e.expectedMemory.MerkleRoot() e.MemoryRoot = e.expectedMemory.MerkleRoot()
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,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/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
...@@ -76,13 +77,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa ...@@ -76,13 +77,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa
} }
if exitGroup { if exitGroup {
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(EndAddr), us.GetState().GetPC(), "must not reach end")
require.True(t, us.GetState().GetExited(), "must set exited state") require.True(t, us.GetState().GetExited(), "must set exited state")
require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1") require.Equal(t, uint8(1), us.GetState().GetExitCode(), "must exit with 1")
} else if expectPanic { } else if expectPanic {
require.NotEqual(t, uint32(EndAddr), us.GetState().GetPC(), "must not reach end") require.NotEqual(t, arch.Word(EndAddr), us.GetState().GetPC(), "must not reach end")
} else { } else {
require.Equal(t, uint32(EndAddr), us.GetState().GetPC(), "must reach end") require.Equal(t, arch.Word(EndAddr), us.GetState().GetPC(), "must reach end")
done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8) done, result := state.GetMemory().GetMemory(BaseAddrEnd+4), state.GetMemory().GetMemory(BaseAddrEnd+8)
// inspect test result // inspect test result
require.Equal(t, done, uint32(1), "must be done") require.Equal(t, done, uint32(1), "must be done")
......
...@@ -27,7 +27,7 @@ func DetectVersion(path string) (StateVersion, error) { ...@@ -27,7 +27,7 @@ func DetectVersion(path string) (StateVersion, error) {
} }
switch ver { switch ver {
case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2: case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64:
return ver, nil return ver, nil
default: default:
return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver) return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver)
......
...@@ -34,6 +34,9 @@ func TestDetectVersion(t *testing.T) { ...@@ -34,6 +34,9 @@ func TestDetectVersion(t *testing.T) {
// Iterate all known versions to ensure we have a test case to detect every state version // Iterate all known versions to ensure we have a test case to detect every state version
for _, version := range StateVersionTypes { for _, version := range StateVersionTypes {
version := version version := version
if version == VersionMultiThreaded64 {
t.Skip("TODO(#12205)")
}
t.Run(version.String(), func(t *testing.T) { t.Run(version.String(), func(t *testing.T) {
testDetection(t, version, ".bin.gz") testDetection(t, version, ".bin.gz")
}) })
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"io" "io"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
...@@ -21,14 +22,16 @@ const ( ...@@ -21,14 +22,16 @@ const (
VersionMultiThreaded VersionMultiThreaded
// VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall // VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall
VersionSingleThreaded2 VersionSingleThreaded2
VersionMultiThreaded64
) )
var ( var (
ErrUnknownVersion = errors.New("unknown version") ErrUnknownVersion = errors.New("unknown version")
ErrJsonNotSupported = errors.New("json not supported") ErrJsonNotSupported = errors.New("json not supported")
ErrUnsupportedMipsArch = errors.New("mips architecture is not supported")
) )
var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2} var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64}
func LoadStateFromFile(path string) (*VersionedState, error) { func LoadStateFromFile(path string) (*VersionedState, error) {
if !serialize.IsBinaryFile(path) { if !serialize.IsBinaryFile(path) {
...@@ -45,15 +48,25 @@ func LoadStateFromFile(path string) (*VersionedState, error) { ...@@ -45,15 +48,25 @@ func LoadStateFromFile(path string) (*VersionedState, error) {
func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) { func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) {
switch state := state.(type) { switch state := state.(type) {
case *singlethreaded.State: case *singlethreaded.State:
if !arch.IsMips32 {
return nil, ErrUnsupportedMipsArch
}
return &VersionedState{ return &VersionedState{
Version: VersionSingleThreaded2, Version: VersionSingleThreaded2,
FPVMState: state, FPVMState: state,
}, nil }, nil
case *multithreaded.State: case *multithreaded.State:
if arch.IsMips32 {
return &VersionedState{ return &VersionedState{
Version: VersionMultiThreaded, Version: VersionMultiThreaded,
FPVMState: state, FPVMState: state,
}, nil }, nil
} else {
return &VersionedState{
Version: VersionMultiThreaded64,
FPVMState: state,
}, nil
}
default: default:
return nil, fmt.Errorf("%w: %T", ErrUnknownVersion, state) return nil, fmt.Errorf("%w: %T", ErrUnknownVersion, state)
} }
...@@ -82,6 +95,9 @@ func (s *VersionedState) Deserialize(in io.Reader) error { ...@@ -82,6 +95,9 @@ func (s *VersionedState) Deserialize(in io.Reader) error {
switch s.Version { switch s.Version {
case VersionSingleThreaded2: case VersionSingleThreaded2:
if !arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &singlethreaded.State{} state := &singlethreaded.State{}
if err := state.Deserialize(in); err != nil { if err := state.Deserialize(in); err != nil {
return err return err
...@@ -89,6 +105,19 @@ func (s *VersionedState) Deserialize(in io.Reader) error { ...@@ -89,6 +105,19 @@ func (s *VersionedState) Deserialize(in io.Reader) error {
s.FPVMState = state s.FPVMState = state
return nil return nil
case VersionMultiThreaded: case VersionMultiThreaded:
if !arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &multithreaded.State{}
if err := state.Deserialize(in); err != nil {
return err
}
s.FPVMState = state
return nil
case VersionMultiThreaded64:
if arch.IsMips32 {
return ErrUnsupportedMipsArch
}
state := &multithreaded.State{} state := &multithreaded.State{}
if err := state.Deserialize(in); err != nil { if err := state.Deserialize(in); err != nil {
return err return err
...@@ -106,6 +135,9 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) { ...@@ -106,6 +135,9 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) {
if s.Version != VersionSingleThreaded { if s.Version != VersionSingleThreaded {
return nil, fmt.Errorf("%w for type %T", ErrJsonNotSupported, s.FPVMState) return nil, fmt.Errorf("%w for type %T", ErrJsonNotSupported, s.FPVMState)
} }
if !arch.IsMips32 {
return nil, ErrUnsupportedMipsArch
}
return json.Marshal(s.FPVMState) return json.Marshal(s.FPVMState)
} }
...@@ -117,6 +149,8 @@ func (s StateVersion) String() string { ...@@ -117,6 +149,8 @@ func (s StateVersion) String() string {
return "multithreaded" return "multithreaded"
case VersionSingleThreaded2: case VersionSingleThreaded2:
return "singlethreaded-2" return "singlethreaded-2"
case VersionMultiThreaded64:
return "multithreaded64"
default: default:
return "unknown" return "unknown"
} }
...@@ -130,6 +164,8 @@ func ParseStateVersion(ver string) (StateVersion, error) { ...@@ -130,6 +164,8 @@ func ParseStateVersion(ver string) (StateVersion, error) {
return VersionMultiThreaded, nil return VersionMultiThreaded, nil
case "singlethreaded-2": case "singlethreaded-2":
return VersionSingleThreaded2, nil return VersionSingleThreaded2, nil
case "multithreaded64":
return VersionMultiThreaded64, nil
default: default:
return StateVersion(0), errors.New("unknown state version") return StateVersion(0), errors.New("unknown state version")
} }
......
...@@ -49,6 +49,10 @@ func TestLoadStateFromFile(t *testing.T) { ...@@ -49,6 +49,10 @@ func TestLoadStateFromFile(t *testing.T) {
}) })
} }
func TestLoadStateFromFile64(t *testing.T) {
t.Skip("TODO(#12205): Test asserting that cannon64 fails to decode a 32-bit state")
}
func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) {
tests := []struct { tests := []struct {
version StateVersion version StateVersion
......
package mipsevm package mipsevm
import "github.com/ethereum/go-ethereum/common" import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum/go-ethereum/common"
)
type LocalContext common.Hash type LocalContext common.Hash
...@@ -13,7 +16,7 @@ type StepWitness struct { ...@@ -13,7 +16,7 @@ type StepWitness struct {
PreimageKey [32]byte // zeroed when no pre-image is accessed PreimageKey [32]byte // zeroed when no pre-image is accessed
PreimageValue []byte // including the 8-byte length prefix PreimageValue []byte // including the 8-byte length prefix
PreimageOffset uint32 PreimageOffset arch.Word
} }
func (wit *StepWitness) HasPreimage() bool { func (wit *StepWitness) HasPreimage() bool {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
) )
...@@ -20,9 +21,7 @@ var vmFS embed.FS ...@@ -20,9 +21,7 @@ var vmFS embed.FS
const baseDir = "embeds" const baseDir = "embeds"
func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error { func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error {
switch ver { if !slices.Contains(versions.StateVersionTypes, ver) {
case versions.VersionSingleThreaded, versions.VersionSingleThreaded2, versions.VersionMultiThreaded:
default:
return errors.New("unsupported version") return errors.New("unsupported version")
} }
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
) )
func Run(ctx *cli.Context) error { func Run(ctx *cli.Context) error {
fmt.Printf("args %v\n", os.Args[:])
if len(os.Args) == 3 && os.Args[2] == "--help" { if len(os.Args) == 3 && os.Args[2] == "--help" {
if err := list(); err != nil { if err := list(); err != nil {
return err return err
......
...@@ -49,6 +49,7 @@ require ( ...@@ -49,6 +49,7 @@ require (
golang.org/x/sync v0.8.0 golang.org/x/sync v0.8.0
golang.org/x/term v0.24.0 golang.org/x/term v0.24.0
golang.org/x/time v0.6.0 golang.org/x/time v0.6.0
lukechampine.com/uint128 v1.3.0
) )
require ( require (
......
...@@ -1098,6 +1098,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh ...@@ -1098,6 +1098,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
......
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