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"; ...@@ -5,6 +5,10 @@ import "./lib/Lib_RLPReader.sol";
interface IMIPS { interface IMIPS {
function Step(bytes32 stateHash) external view returns (bytes32); 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 ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32); function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32);
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32); function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
...@@ -48,7 +52,7 @@ contract Challenge { ...@@ -48,7 +52,7 @@ contract Challenge {
for (uint32 i = 0; i < 32; i += 4) { for (uint32 i = 0; i < 32; i += 4) {
uint256 tv = uint256(val>>(224-(i*8))); 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; return stateHash;
} }
...@@ -104,8 +108,8 @@ contract Challenge { ...@@ -104,8 +108,8 @@ contract Challenge {
// confirm the finalSystemHash asserts the state you claim (in $t0-$t7) and the machine is stopped // 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 // you must load these proofs into MIPS before calling this
// we disagree at the end // we disagree at the end
require(mips.ReadBytes32(finalSystemState, 0x30000800) == assertionRoot, "you are claiming a different state root in machine"); require(mips.m().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().ReadMemory(finalSystemState, 0xC0000080) == 0xDEAD0000, "machine is not stopped in final state (PC == 0xDEAD0000)");
return newChallengeTrusted(startState, finalSystemState, stepCount); return newChallengeTrusted(startState, finalSystemState, stepCount);
} }
......
...@@ -10,74 +10,23 @@ pragma solidity ^0.7.3; ...@@ -10,74 +10,23 @@ pragma solidity ^0.7.3;
// If you are using the Preimage oracle, you call AddPreimage // 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 // Then, you call Step. Step will revert if state is missing. If all state is present, it will return the next hash
contract MIPS { interface IMIPSMemory {
// This state is global function ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
mapping(bytes32 => mapping (uint32 => uint64)) public state; function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32);
mapping(bytes32 => bytes) public preimage; function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
}
function AddPreimage(bytes calldata anything) public {
preimage[keccak256(anything)] = anything;
}
function AddMerkleState(bytes32 stateHash, uint32 addr, uint32 value, string calldata proof) public { contract MIPS {
// TODO: check proof IMIPSMemory constant public m = IMIPSMemory(0x1338);
state[stateHash][addr] = (1 << 32) | value;
}
uint32 constant public REG_OFFSET = 0xc0000000; uint32 constant public REG_OFFSET = 0xc0000000;
uint32 constant public REG_PC = REG_OFFSET + 0x20*4; 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 // will revert if any required input state is missing
function Step(bytes32 stateHash) public view returns (bytes32) { function Step(bytes32 stateHash) public view returns (bytes32) {
// instruction fetch // instruction fetch
uint32 pc = ReadMemory(stateHash, REG_PC); uint32 pc = m.ReadMemory(stateHash, REG_PC);
uint32 insn = ReadMemory(stateHash, pc); uint32 insn = m.ReadMemory(stateHash, pc);
uint32 opcode = insn >> 26; // 6-bits uint32 opcode = insn >> 26; // 6-bits
// decode // decode
...@@ -87,10 +36,10 @@ contract MIPS { ...@@ -87,10 +36,10 @@ contract MIPS {
uint32 rt; uint32 rt;
if (opcode != 2 && opcode != 3) { // J-type: j and jal have no register fetch if (opcode != 2 && opcode != 3) { // J-type: j and jal have no register fetch
// R-type or I-type (stores rt) // 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) { if (opcode == 0) {
// R-type (stores rd) // 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 { ...@@ -100,7 +49,7 @@ contract MIPS {
if (opcode >= 0x20) { if (opcode >= 0x20) {
// M[R[rs]+SignExtImm] // M[R[rs]+SignExtImm]
uint32 SignExtImm = insn&0xFFFF | (insn&0x8000 != 0 ? 0xFFFF0000 : 0); uint32 SignExtImm = insn&0xFFFF | (insn&0x8000 != 0 ? 0xFFFF0000 : 0);
mem = ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC); mem = m.ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
} }
// execute // 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 ( ...@@ -28,23 +28,30 @@ import (
) )
// **** stub Tracer **** // **** 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. // 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. // 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 // 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. // 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 // Config are the configuration options for the Interpreter
type Config struct { type Config struct {
......
#!/bin/bash
(cd ../ && npx hardhat compile) && go build && ./mipsevm
package main package main
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"log"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum/common" "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/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
type StateDB struct { type StateDB struct {
Bytecode []byte
} }
func (s *StateDB) AddAddressToAccessList(addr common.Address) {} func (s *StateDB) AddAddressToAccessList(addr common.Address) {}
...@@ -27,7 +35,10 @@ func (b *StateDB) ForEachStorage(addr common.Address, cb func(key, value common. ...@@ -27,7 +35,10 @@ func (b *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.
return nil return nil
} }
func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 } 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) GetCodeHash(addr common.Address) common.Hash { return common.Hash{} }
func (s *StateDB) GetCodeSize(addr common.Address) int { return 0 } func (s *StateDB) GetCodeSize(addr common.Address) int { return 0 }
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { 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 ...@@ -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) GetNonce(addr common.Address) uint64 { return 0 }
func (s *StateDB) GetRefund() 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) HasSuicided(addr common.Address) bool { return false }
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { 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) {} ...@@ -51,15 +65,74 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {}
func (s *StateDB) SubRefund(gas uint64) {} func (s *StateDB) SubRefund(gas uint64) {}
func (s *StateDB) Suicide(addr common.Address) bool { return true } 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() { func main() {
fmt.Println("hello") fmt.Println("hello")
/*var parent types.Header /*var parent types.Header
database := state.NewDatabase(parent) database := state.NewDatabase(parent)
statedb, _ := state.New(parent.Root, database, nil)*/ 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{} config := vm.Config{}
vm := vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, statedb, params.MainnetChainConfig, config) config.Debug = true
fmt.Println(vm) 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