Commit 4bd7f328 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

Merge pull request #8156 from ethereum-optimism/aj/cannon-abi

cannon: Use ABIs to encode contract calls.
parents e0441341 d6d01af1
......@@ -2,10 +2,8 @@ package mipsevm
import (
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
......@@ -22,27 +20,6 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
)
var (
StepBytes4 []byte
LoadKeccak256PreimagePartBytes4 []byte
LoadLocalDataBytes4 []byte
)
func init() {
mipsAbi, err := bindings.MIPSMetaData.GetAbi()
if err != nil {
panic(fmt.Errorf("failed to load MIPS ABI: %w", err))
}
StepBytes4 = mipsAbi.Methods["step"].ID[:4]
preimageAbi, err := bindings.PreimageOracleMetaData.GetAbi()
if err != nil {
panic(fmt.Errorf("failed to load pre-image oracle ABI: %w", err))
}
LoadKeccak256PreimagePartBytes4 = preimageAbi.Methods["loadKeccak256PreimagePart"].ID[:4]
LoadLocalDataBytes4 = preimageAbi.Methods["loadLocalData"].ID[:4]
}
// LoadContracts loads the Cannon contracts, from op-bindings package
func LoadContracts() (*Contracts, error) {
var mips, oracle Contract
......@@ -56,34 +33,6 @@ func LoadContracts() (*Contracts, error) {
}, nil
}
// LoadContractsFromFiles loads the Cannon contracts, from local filesystem
func LoadContractsFromFiles() (*Contracts, error) {
mips, err := LoadContract("MIPS")
if err != nil {
return nil, err
}
oracle, err := LoadContract("PreimageOracle")
if err != nil {
return nil, err
}
return &Contracts{
MIPS: mips,
Oracle: oracle,
}, nil
}
func LoadContract(name string) (*Contract, error) {
dat, err := os.ReadFile(fmt.Sprintf("../../packages/contracts-bedrock/forge-artifacts/%s.sol/%s.json", name, name))
if err != nil {
return nil, fmt.Errorf("failed to read contract JSON definition of %q: %w", name, err)
}
var out Contract
if err := json.Unmarshal(dat, &out); err != nil {
return nil, fmt.Errorf("failed to parse contract JSON definition of %q: %w", name, err)
}
return &out, nil
}
type Contract struct {
DeployedBytecode struct {
Object hexutil.Bytes `json:"object"`
......
......@@ -3,6 +3,8 @@ package mipsevm
import (
"bytes"
"debug/elf"
"errors"
"fmt"
"io"
"math/big"
"os"
......@@ -11,6 +13,8 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
......@@ -76,13 +80,13 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := stepWitness.EncodePreimageOracleInput(0)
poInput, err := encodePreimageOracleInput(t, stepWitness, 0)
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
input := stepWitness.EncodeStepInput(0)
input := encodeStepInput(t, stepWitness, 0)
ret, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash")
......@@ -101,6 +105,53 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
return evmPost
}
func encodeStepInput(t *testing.T, wit *StepWitness, localContext LocalContext) []byte {
mipsAbi, err := bindings.MIPSMetaData.GetAbi()
require.NoError(t, err)
input, err := mipsAbi.Pack("step", wit.State, wit.MemProof, new(big.Int).SetUint64(uint64(localContext)))
require.NoError(t, err)
return input
}
func encodePreimageOracleInput(t *testing.T, wit *StepWitness, localContext LocalContext) ([]byte, error) {
if wit.PreimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof")
}
preimageAbi, err := bindings.PreimageOracleMetaData.GetAbi()
require.NoError(t, err, "failed to load pre-image oracle ABI")
switch preimage.KeyType(wit.PreimageKey[0]) {
case preimage.LocalKeyType:
if len(wit.PreimageValue) > 32+8 {
return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", wit.PreimageKey)
}
preimagePart := wit.PreimageValue[8:]
var tmp [32]byte
copy(tmp[:], preimagePart)
input, err := preimageAbi.Pack("loadLocalData",
new(big.Int).SetBytes(wit.PreimageKey[1:]),
new(big.Int).SetUint64(uint64(localContext)),
tmp,
new(big.Int).SetUint64(uint64(len(preimagePart))),
new(big.Int).SetUint64(uint64(wit.PreimageOffset)),
)
require.NoError(t, err)
return input, nil
case preimage.Keccak256KeyType:
input, err := preimageAbi.Pack(
"loadKeccak256PreimagePart",
new(big.Int).SetUint64(uint64(wit.PreimageOffset)),
wit.PreimageValue[8:])
require.NoError(t, err)
return input, nil
default:
return nil, fmt.Errorf("unsupported pre-image type %d, cannot prepare preimage with key %x offset %d for oracle",
wit.PreimageKey[0], wit.PreimageKey, wit.PreimageOffset)
}
}
func TestEVM(t *testing.T) {
testFiles, err := os.ReadDir("open_mips_tests/test/bin")
require.NoError(t, err)
......@@ -241,7 +292,7 @@ func TestEVMFault(t *testing.T) {
State: initialState.EncodeWitness(),
MemProof: insnProof[:],
}
input := stepWitness.EncodeStepInput(0)
input := encodeStepInput(t, stepWitness, 0)
startingGas := uint64(30_000_000)
_, _, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0))
......
package mipsevm
import (
"encoding/binary"
"errors"
"fmt"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
type LocalContext uint64
type StepWitness struct {
......@@ -21,78 +13,6 @@ type StepWitness struct {
PreimageOffset uint32
}
func uint32ToBytes32(v uint32) []byte {
var out [32]byte
binary.BigEndian.PutUint32(out[32-4:], v)
return out[:]
}
func uint64ToBytes32(v uint64) []byte {
var out [32]byte
binary.BigEndian.PutUint64(out[32-8:], v)
return out[:]
}
func (wit *StepWitness) EncodeStepInput(localContext LocalContext) []byte {
abiStateLen := len(wit.State)
if abiStateLen%32 != 0 {
abiStateLen += 32 - (abiStateLen % 32)
}
// pad state to 32 byte multiple per ABI
abiState := make([]byte, abiStateLen)
copy(abiState, wit.State)
var input []byte
input = append(input, StepBytes4...)
input = append(input, uint32ToBytes32(32*3)...) // state data offset in bytes
input = append(input, uint32ToBytes32(32*3+32+uint32(len(abiState)))...) // proof data offset in bytes
input = append(input, uint64ToBytes32(uint64(localContext))...) // local context in bytes
input = append(input, uint32ToBytes32(uint32(len(wit.State)))...) // state data length in bytes
input = append(input, abiState[:]...)
input = append(input, uint32ToBytes32(uint32(len(wit.MemProof)))...) // proof data length in bytes
input = append(input, wit.MemProof[:]...)
return input
}
func (wit *StepWitness) HasPreimage() bool {
return wit.PreimageKey != ([32]byte{})
}
func (wit *StepWitness) EncodePreimageOracleInput(localContext LocalContext) ([]byte, error) {
if wit.PreimageKey == ([32]byte{}) {
return nil, errors.New("cannot encode pre-image oracle input, witness has no pre-image to proof")
}
switch preimage.KeyType(wit.PreimageKey[0]) {
case preimage.LocalKeyType:
if len(wit.PreimageValue) > 32+8 {
return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", wit.PreimageKey)
}
var input []byte
input = append(input, LoadLocalDataBytes4...)
input = append(input, wit.PreimageKey[:]...)
input = append(input, uint64ToBytes32(uint64(localContext))...) // local context in bytes
preimagePart := wit.PreimageValue[8:]
var tmp [32]byte
copy(tmp[:], preimagePart)
input = append(input, tmp[:]...)
input = append(input, uint32ToBytes32(uint32(len(wit.PreimageValue)-8))...)
input = append(input, uint32ToBytes32(wit.PreimageOffset)...)
// Note: we can pad calldata to 32 byte multiple, but don't strictly have to
return input, nil
case preimage.Keccak256KeyType:
var input []byte
input = append(input, LoadKeccak256PreimagePartBytes4...)
input = append(input, uint32ToBytes32(wit.PreimageOffset)...)
input = append(input, uint32ToBytes32(32+32)...) // partOffset, calldata offset
input = append(input, uint32ToBytes32(uint32(len(wit.PreimageValue))-8)...)
input = append(input, wit.PreimageValue[8:]...)
// Note: we can pad calldata to 32 byte multiple, but don't strictly have to
return input, nil
default:
return nil, fmt.Errorf("unsupported pre-image type %d, cannot prepare preimage with key %x offset %d for oracle",
wit.PreimageKey[0], wit.PreimageKey, wit.PreimageOffset)
}
}
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