Commit 628f5b90 authored by George Hotz's avatar George Hotz

factor into MIPSMemory, fix Tracer

parent ec6d8872
......@@ -5,6 +5,10 @@ import "./lib/Lib_RLPReader.sol";
interface IMIPS {
function Step(bytes32 stateHash) external view returns (bytes32);
function m() external pure returns (IMIPSMemory);
}
interface IMIPSMemory {
function ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32);
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
......@@ -48,7 +52,7 @@ contract Challenge {
for (uint32 i = 0; i < 32; i += 4) {
uint256 tv = uint256(val>>(224-(i*8)));
stateHash = mips.WriteMemory(stateHash, addr+i, uint32(tv));
stateHash = mips.m().WriteMemory(stateHash, addr+i, uint32(tv));
}
return stateHash;
}
......@@ -104,8 +108,8 @@ contract Challenge {
// confirm the finalSystemHash asserts the state you claim (in $t0-$t7) and the machine is stopped
// you must load these proofs into MIPS before calling this
// we disagree at the end
require(mips.ReadBytes32(finalSystemState, 0x30000800) == assertionRoot, "you are claiming a different state root in machine");
require(mips.ReadMemory(finalSystemState, 0xC0000080) == 0xDEAD0000, "machine is not stopped in final state (PC == 0xDEAD0000)");
require(mips.m().ReadBytes32(finalSystemState, 0x30000800) == assertionRoot, "you are claiming a different state root in machine");
require(mips.m().ReadMemory(finalSystemState, 0xC0000080) == 0xDEAD0000, "machine is not stopped in final state (PC == 0xDEAD0000)");
return newChallengeTrusted(startState, finalSystemState, stepCount);
}
......
......@@ -10,74 +10,23 @@ pragma solidity ^0.7.3;
// If you are using the Preimage oracle, you call AddPreimage
// Then, you call Step. Step will revert if state is missing. If all state is present, it will return the next hash
contract MIPS {
// This state is global
mapping(bytes32 => mapping (uint32 => uint64)) public state;
mapping(bytes32 => bytes) public preimage;
function AddPreimage(bytes calldata anything) public {
preimage[keccak256(anything)] = anything;
}
interface IMIPSMemory {
function ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32);
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
}
function AddMerkleState(bytes32 stateHash, uint32 addr, uint32 value, string calldata proof) public {
// TODO: check proof
state[stateHash][addr] = (1 << 32) | value;
}
contract MIPS {
IMIPSMemory constant public m = IMIPSMemory(0x1338);
uint32 constant public REG_OFFSET = 0xc0000000;
uint32 constant public REG_PC = REG_OFFSET + 0x20*4;
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) public pure returns (bytes32) {
// TODO: does the stateHash mutation
require(addr & 3 == 0, "write memory must be 32-bit aligned");
}
// needed for preimage oracle
function ReadBytes32(bytes32 stateHash, uint32 addr) public view returns (bytes32) {
uint256 ret = 0;
for (uint32 i = 0; i < 32; i += 4) {
ret <<= 32;
ret |= uint256(ReadMemory(stateHash, addr+i));
}
return bytes32(ret);
}
function ReadMemory(bytes32 stateHash, uint32 addr) public view returns (uint32) {
require(addr & 3 == 0, "read memory must be 32-bit aligned");
// zero register is always 0
if (addr == REG_OFFSET) {
return 0;
}
// MMIO preimage oracle
if (addr >= 0x31000000 && addr < 0x32000000) {
bytes32 pihash = ReadBytes32(stateHash, 0x30001000);
if (addr == 0x31000000) {
return uint32(preimage[pihash].length);
}
uint offset = addr-0x31000004;
uint8 a0 = uint8(preimage[pihash][offset]);
uint8 a1 = uint8(preimage[pihash][offset+1]);
uint8 a2 = uint8(preimage[pihash][offset+2]);
uint8 a3 = uint8(preimage[pihash][offset+3]);
return (uint32(a0) << 24) |
(uint32(a1) << 16) |
(uint32(a2) << 8) |
(uint32(a3) << 0);
}
uint64 ret = state[stateHash][addr];
require((ret >> 32) == 1, "memory was not initialized");
return uint32(ret);
}
// compute the next state
// will revert if any required input state is missing
function Step(bytes32 stateHash) public view returns (bytes32) {
// instruction fetch
uint32 pc = ReadMemory(stateHash, REG_PC);
uint32 insn = ReadMemory(stateHash, pc);
uint32 pc = m.ReadMemory(stateHash, REG_PC);
uint32 insn = m.ReadMemory(stateHash, pc);
uint32 opcode = insn >> 26; // 6-bits
// decode
......@@ -87,10 +36,10 @@ contract MIPS {
uint32 rt;
if (opcode != 2 && opcode != 3) { // J-type: j and jal have no register fetch
// R-type or I-type (stores rt)
rs = ReadMemory(stateHash, REG_OFFSET + ((insn >> 19) & 0x7C));
rs = m.ReadMemory(stateHash, REG_OFFSET + ((insn >> 19) & 0x7C));
if (opcode == 0) {
// R-type (stores rd)
rt = ReadMemory(stateHash, REG_OFFSET + ((insn >> 14) & 0x7C));
rt = m.ReadMemory(stateHash, REG_OFFSET + ((insn >> 14) & 0x7C));
}
}
......@@ -100,7 +49,7 @@ contract MIPS {
if (opcode >= 0x20) {
// M[R[rs]+SignExtImm]
uint32 SignExtImm = insn&0xFFFF | (insn&0x8000 != 0 ? 0xFFFF0000 : 0);
mem = ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
mem = m.ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
}
// execute
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.3;
contract MIPSMemory {
// This state is global
mapping(bytes32 => mapping (uint32 => uint64)) public state;
mapping(bytes32 => bytes) public preimage;
function AddPreimage(bytes calldata anything) public {
preimage[keccak256(anything)] = anything;
}
function AddMerkleState(bytes32 stateHash, uint32 addr, uint32 value, string calldata proof) public {
// TODO: check proof
state[stateHash][addr] = (1 << 32) | value;
}
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) public pure returns (bytes32) {
// TODO: does the stateHash mutation
require(addr & 3 == 0, "write memory must be 32-bit aligned");
}
// needed for preimage oracle
function ReadBytes32(bytes32 stateHash, uint32 addr) public view returns (bytes32) {
uint256 ret = 0;
for (uint32 i = 0; i < 32; i += 4) {
ret <<= 32;
ret |= uint256(ReadMemory(stateHash, addr+i));
}
return bytes32(ret);
}
function ReadMemory(bytes32 stateHash, uint32 addr) public view returns (uint32) {
require(addr & 3 == 0, "read memory must be 32-bit aligned");
// zero register is always 0
if (addr == 0xc0000000) {
return 0;
}
// MMIO preimage oracle
if (addr >= 0x31000000 && addr < 0x32000000) {
bytes32 pihash = ReadBytes32(stateHash, 0x30001000);
if (addr == 0x31000000) {
return uint32(preimage[pihash].length);
}
uint offset = addr-0x31000004;
uint8 a0 = uint8(preimage[pihash][offset]);
uint8 a1 = uint8(preimage[pihash][offset+1]);
uint8 a2 = uint8(preimage[pihash][offset+2]);
uint8 a3 = uint8(preimage[pihash][offset+3]);
return (uint32(a0) << 24) |
(uint32(a1) << 16) |
(uint32(a2) << 8) |
(uint32(a3) << 0);
}
uint64 ret = state[stateHash][addr];
require((ret >> 32) == 1, "memory was not initialized");
return uint32(ret);
}
}
\ No newline at end of file
......@@ -28,23 +28,30 @@ import (
)
// **** stub Tracer ****
type Tracer struct{}
type Tracer interface {
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
}
/*type StubTracer struct{}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (jst *Tracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (jst *StubTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
func (jst *Tracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
func (jst *StubTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
}
// CaptureFault implements the Tracer interface to trace an execution fault
func (jst *Tracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
func (jst *StubTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
}
func (jst *StubTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
}*/
// Config are the configuration options for the Interpreter
type Config struct {
......
#!/bin/bash
(cd ../ && npx hardhat compile) && go build && ./mipsevm
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
type StateDB struct {
Bytecode []byte
}
func (s *StateDB) AddAddressToAccessList(addr common.Address) {}
......@@ -27,7 +35,10 @@ func (b *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.
return nil
}
func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 }
func (s *StateDB) GetCode(addr common.Address) []byte { return []byte{} }
func (s *StateDB) GetCode(addr common.Address) []byte {
fmt.Println("GetCode", addr)
return s.Bytecode
}
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { return common.Hash{} }
func (s *StateDB) GetCodeSize(addr common.Address) int { return 0 }
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
......@@ -35,7 +46,10 @@ func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) commo
}
func (s *StateDB) GetNonce(addr common.Address) uint64 { return 0 }
func (s *StateDB) GetRefund() uint64 { return 0 }
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { return common.Hash{} }
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
fmt.Println("GetState", addr, hash)
return common.Hash{}
}
func (s *StateDB) HasSuicided(addr common.Address) bool { return false }
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
}
......@@ -51,15 +65,74 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {}
func (s *StateDB) SubRefund(gas uint64) {}
func (s *StateDB) Suicide(addr common.Address) bool { return true }
// **** stub Tracer ****
type Tracer struct{}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
fmt.Println(pc, op, gas)
}
// CaptureFault implements the Tracer interface to trace an execution fault
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
}
type jsoncontract struct {
Bytecode string `json:"bytecode"`
DeployedBytecode string `json:"deployedBytecode"`
}
func main() {
fmt.Println("hello")
/*var parent types.Header
database := state.NewDatabase(parent)
statedb, _ := state.New(parent.Root, database, nil)*/
statedb := &StateDB{}
var jj jsoncontract
mipsjson, _ := ioutil.ReadFile("../artifacts/contracts/MIPS.sol/MIPS.json")
json.NewDecoder(bytes.NewReader(mipsjson)).Decode(&jj)
bytecode := common.Hex2Bytes(jj.DeployedBytecode[2:])
//fmt.Println(bytecode, jj.Bytecode)
bytecodehash := crypto.Keccak256Hash(bytecode)
statedb := &StateDB{Bytecode: bytecode}
bc := core.NewBlockChain()
var header types.Header
header.Number = big.NewInt(13284469)
header.Difficulty = common.Big0
author := common.Address{}
blockContext := core.NewEVMBlockContext(&header, bc, &author)
txContext := vm.TxContext{}
config := vm.Config{}
vm := vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, statedb, params.MainnetChainConfig, config)
fmt.Println(vm)
config.Debug = true
tracer := Tracer{}
config.Tracer = &tracer
evm := vm.NewEVM(blockContext, txContext, statedb, params.MainnetChainConfig, config)
fmt.Println(evm)
from := common.Address{}
to := common.HexToAddress("0x1337")
/*ret, gas, err := evm.Call(vm.AccountRef(from), to, []byte{}, 20000000, common.Big0)
fmt.Println(ret, gas, err)*/
interpreter := vm.NewEVMInterpreter(evm, config)
input := []byte{0x69, 0x37, 0x33, 0x72} // Step(bytes32)
input = append(input, common.Hash{}.Bytes()...)
contract := vm.NewContract(vm.AccountRef(from), vm.AccountRef(to), common.Big0, 20000000)
//fmt.Println(bytecodehash, bytecode)
contract.SetCallCode(&to, bytecodehash, bytecode)
ret, err := interpreter.Run(contract, input, false)
fmt.Println(ret, err, contract.Gas)
if err != nil {
log.Fatal(err)
}
}
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