Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

cannon: Implement 64-bit Solidity VM #12665

Merged
merged 14 commits into from
Oct 30, 2024
5 changes: 4 additions & 1 deletion cannon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@ sanitize-program:
contract:
cd ../packages/contracts-bedrock && forge build

test: elf contract
test: elf contract test64
go test -v ./...

test64: elf contract
go test -tags=cannon64 -run '(TestEVM.*64|TestHelloEVM|TestClaimEVM)' ./mipsevm/tests

diff-%-cannon: cannon elf
$$OTHER_CANNON load-elf --type $* --path ./testdata/example/bin/hello.elf --out ./bin/prestate-other.bin.gz --meta ""
./bin/cannon load-elf --type $* --path ./testdata/example/bin/hello.elf --out ./bin/prestate.bin.gz --meta ""
Expand Down
3 changes: 2 additions & 1 deletion cannon/mipsevm/exec/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package exec
import (
"fmt"

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

Expand Down Expand Up @@ -37,7 +38,7 @@ func (m *MemoryTrackerImpl) TrackMemAccess(effAddr Word) {
// 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
func (m *MemoryTrackerImpl) TrackMemAccess2(effAddr Word) {
if m.memProofEnabled && m.lastMemAccess+4 != effAddr {
if m.memProofEnabled && m.lastMemAccess+arch.WordSizeBytes != effAddr {
panic(fmt.Errorf("unexpected disjointed mem access at %08x, last memory access is at %08x buffered", effAddr, m.lastMemAccess))
}
m.lastMemAccess = effAddr
Expand Down
12 changes: 8 additions & 4 deletions cannon/mipsevm/exec/mips_instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const (
)

func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun uint32) {
insn = memory.GetUint32(pc)
if pc&0x3 != 0 {
panic(fmt.Errorf("invalid pc: %x", pc))
}
word := memory.GetWord(pc & arch.AddressMask)
insn = uint32(SelectSubWord(pc, word, 4, false))
opcode = insn >> 26 // First 6-bits
fun = insn & 0x3f // Last 6-bits

Expand Down Expand Up @@ -374,7 +378,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem
w := uint32(SelectSubWord(rs, mem, 4, false))
val := w >> (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)
lwrResult := ((uint32(rt) & ^mask) | val) & 0xFFFFFFFF
lwrResult := (uint32(rt) & ^mask) | val
if rs&3 == 3 { // loaded bit 31
return SignExtend(Word(lwrResult), 32)
} else {
Expand Down Expand Up @@ -530,13 +534,13 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor
cpu.HI = SignExtend(Word(acc>>32), 32)
cpu.LO = SignExtend(Word(uint32(acc)), 32)
case 0x1a: // div
if rt == 0 {
if uint32(rt) == 0 {
Inphi marked this conversation as resolved.
Show resolved Hide resolved
panic("instruction divide by zero")
}
cpu.HI = SignExtend(Word(int32(rs)%int32(rt)), 32)
cpu.LO = SignExtend(Word(int32(rs)/int32(rt)), 32)
case 0x1b: // divu
if rt == 0 {
if uint32(rt) == 0 {
Inphi marked this conversation as resolved.
Show resolved Hide resolved
panic("instruction divide by zero")
}
cpu.HI = SignExtend(Word(uint32(rs)%uint32(rt)), 32)
Expand Down
28 changes: 15 additions & 13 deletions cannon/mipsevm/tests/evm_common64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -134,12 +133,12 @@ func TestEVMSingleStep_Operators64(t *testing.T) {

// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}

func TestEVMSingleStep_Shift(t *testing.T) {
func TestEVMSingleStep_Shift64(t *testing.T) {
cases := []struct {
name string
rd Word
Expand Down Expand Up @@ -190,7 +189,8 @@ func TestEVMSingleStep_Shift(t *testing.T) {
for i, tt := range cases {
testName := fmt.Sprintf("%v %v", v.Name, tt.name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0))
pc := Word(0x0)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(pc))
state := goVm.GetState()
var insn uint32
var rtReg uint32
Expand All @@ -200,7 +200,7 @@ func TestEVMSingleStep_Shift(t *testing.T) {
insn = rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct
state.GetRegistersRef()[rdReg] = tt.rd
state.GetRegistersRef()[rtReg] = tt.rt
testutil.StoreInstruction(state.GetMemory(), 0, insn)
testutil.StoreInstruction(state.GetMemory(), pc, insn)
step := state.GetStep()

// Setup expectations
Expand All @@ -213,7 +213,7 @@ func TestEVMSingleStep_Shift(t *testing.T) {

// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
Expand Down Expand Up @@ -455,12 +455,12 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) {

// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}

func TestEVMSingleStep_DivMult(t *testing.T) {
func TestEVMSingleStep_DivMult64(t *testing.T) {
cases := []struct {
name string
rs Word
Expand Down Expand Up @@ -530,6 +530,10 @@ func TestEVMSingleStep_DivMult(t *testing.T) {
{name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0},
{name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: 2, expectLo: 0x7F_FF_FF_FF_FF_FF_FF_FF, expectHi: 1},
{name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0, expectHi: 0x7F_FF_FF_FF_00_00_00_00},

// a couple div/divu 64-bit edge cases
{name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"},
{name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"},
}

v := GetMultiThreadedTestCase(t)
Expand Down Expand Up @@ -560,15 +564,13 @@ func TestEVMSingleStep_DivMult(t *testing.T) {
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, nil)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
}
})
}
}

func TestEVMSingleStepBranch64(t *testing.T) {
var tracer *tracing.Hooks

func TestEVMSingleStep_Branch64(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
Expand Down Expand Up @@ -651,7 +653,7 @@ func TestEVMSingleStepBranch64(t *testing.T) {

// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts, tracer)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
Expand Down
Loading