Commit 121e9448 authored by George Hotz's avatar George Hotz

minievm refactor, loads in the preimages to mipsmemory now

parent 1037f881
package main
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
)
func deploy(interpreter *vm.EVMInterpreter, statedb *StateDB) {
bytecode := GetBytecode(false)
from := common.Address{}
to := common.Address{}
gas := uint64(10000000)
input := make([]byte, 0)
contract := vm.NewContract(vm.AccountRef(from), vm.AccountRef(to), common.Big0, gas)
contract.SetCallCode(&to, crypto.Keccak256Hash(bytecode), bytecode)
ret, err := interpreter.Run(contract, input, false)
check(err)
fmt.Println("returned", len(ret))
statedb.Bytecodes[common.HexToAddress("0x1337")] = ret
}
func addTrieNode(str []byte, interpreter *vm.EVMInterpreter, statedb *StateDB) {
from := common.Address{}
to := common.HexToAddress("0xBd770416a3345F91E4B34576cb804a576fa48EB1")
gas := uint64(100000000)
input := crypto.Keccak256Hash([]byte("AddTrieNode(bytes)")).Bytes()[:4]
// offset
input = append(input, common.BigToHash(big.NewInt(int64(0x20))).Bytes()...)
// length
input = append(input, common.BigToHash(big.NewInt(int64(len(str)))).Bytes()...)
input = append(input, str...)
input = append(input, make([]byte, 0x20-(len(input)%0x20))...)
bytecode := statedb.Bytecodes[to]
//fmt.Println("bytecode", len(bytecode))
contract := vm.NewContract(vm.AccountRef(from), vm.AccountRef(to), common.Big0, gas)
contract.SetCallCode(&to, crypto.Keccak256Hash(bytecode), bytecode)
_, err := interpreter.Run(contract, input, false)
check(err)
}
func RunFull() {
interpreter, statedb := GetInterpreter(0, true)
deploy(interpreter, statedb)
ram := make(map[uint32](uint32))
//LoadMappedFile("../mipigo/test/test.bin", ram, 0)
LoadMappedFile("test/bin/add.bin", ram, 0)
ZeroRegisters(ram)
ram[0xC000007C] = 0x5EAD0000
root := RamToTrie(ram)
fmt.Println("state root", root, "nodes", len(Preimages))
//ParseNode(root, 0)
for _, v := range Preimages {
addTrieNode(v, interpreter, statedb)
}
}
package main
import (
"testing"
)
func TestFull(t *testing.T) {
RunFull()
}
......@@ -25,6 +25,12 @@ func LoadMappedFile(fn string, ram map[uint32](uint32), base uint32) {
LoadData(dat, ram, base)
}
func ZeroRegisters(ram map[uint32](uint32)) {
for i := uint32(0xC0000000); i < 0xC0000000+36*4; i += 4 {
WriteRam(ram, i, 0)
}
}
func RunMinigeth(fn string, steps int, debug int) {
ram := make(map[uint32](uint32))
LoadMappedFile(fn, ram, 0)
......
package main
import (
"encoding/binary"
"fmt"
"io/ioutil"
"math/big"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
var callback func(int, map[uint32](uint32))
func bytesTo32(a []byte) uint32 {
//return uint32(common.BytesToHash(a).Big().Uint64())
return binary.BigEndian.Uint32(a[28:])
}
type StateDB struct {
Bytecodes map[common.Address]([]byte)
RealState map[common.Hash](common.Hash)
Ram map[uint32](uint32)
Debug int
PcCount int
seenWrite bool
useRealState bool
}
func NewStateDB(debug int, realState bool) *StateDB {
statedb := &StateDB{}
statedb.Bytecodes = make(map[common.Address]([]byte))
statedb.RealState = make(map[common.Hash](common.Hash))
statedb.Debug = debug
statedb.seenWrite = true
statedb.useRealState = realState
return statedb
}
func (s *StateDB) AddAddressToAccessList(addr common.Address) {}
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {}
func (s *StateDB) AddLog(log *types.Log) {}
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {}
func (s *StateDB) AddRefund(gas uint64) {}
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {}
func (s *StateDB) AddressInAccessList(addr common.Address) bool { return true }
func (s *StateDB) CreateAccount(addr common.Address) {}
func (s *StateDB) Empty(addr common.Address) bool { return false }
func (s *StateDB) Exist(addr common.Address) bool { return true }
func (b *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
return nil
}
func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 }
func (s *StateDB) GetCode(addr common.Address) []byte {
fmt.Println("GetCode", addr)
return s.Bytecodes[addr]
}
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { return common.Hash{} }
func (s *StateDB) GetCodeSize(addr common.Address) int { return 100 }
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
return common.Hash{}
}
func (s *StateDB) GetNonce(addr common.Address) uint64 { return 0 }
func (s *StateDB) GetRefund() uint64 { return 0 }
func (s *StateDB) GetState(fakeaddr common.Address, hash common.Hash) common.Hash {
if s.useRealState {
// TODO: fakeaddr?
return s.RealState[hash]
}
ram := s.Ram
//fmt.Println("GetState", addr, hash)
addr := bytesTo32(hash.Bytes())
nret := ram[addr]
mret := make([]byte, 32)
binary.BigEndian.PutUint32(mret[0x1c:], nret)
if s.Debug >= 2 {
fmt.Println("HOOKED READ! ", fmt.Sprintf("%x = %x", addr, nret))
}
if addr == 0xc0000080 && s.seenWrite {
if s.Debug >= 1 {
fmt.Printf("%7d %8X %08X : %08X %08X %08X %08X %08X %08X %08X %08X %08X\n",
s.PcCount, nret&0x7FFFFFFF, ram[nret],
ram[0xc0000004],
ram[0xc0000008], ram[0xc000000c], ram[0xc0000010], ram[0xc0000014],
ram[0xc0000018], ram[0xc000001c], ram[0xc0000020], ram[0xc0000024])
}
if (s.PcCount % 100000) == 0 {
steps_per_sec := float64(s.PcCount) * 1e9 / float64(time.Now().Sub(ministart).Nanoseconds())
os.Stderr.WriteString(fmt.Sprintf("%10d pc: %x steps per s %f ram entries %d\n", s.PcCount, nret&0x7FFFFFFF, steps_per_sec, len(ram)))
}
if callback != nil {
callback(s.PcCount, ram)
}
if ram[nret] == 0xC {
syscall := ram[0xc0000008]
if syscall == 4020 {
oracle_hash := make([]byte, 0x20)
for i := uint32(0); i < 0x20; i += 4 {
binary.BigEndian.PutUint32(oracle_hash[i:i+4], ram[0x30001000+i])
}
hash := common.BytesToHash(oracle_hash)
key := fmt.Sprintf("/tmp/eth/%s", hash)
value, _ := ioutil.ReadFile(key)
WriteRam(ram, 0x31000000, uint32(len(value)))
value = append(value, 0, 0, 0)
for i := uint32(0); i < ram[0x31000000]; i += 4 {
WriteRam(ram, 0x31000004+i, binary.BigEndian.Uint32(value[i:i+4]))
}
} else if syscall == 4004 {
len := ram[0xc0000018]
buf := make([]byte, len+0x10)
addr := ram[0xc0000014]
offset := addr & 3
for i := uint32(0); i < offset+len; i += 4 {
binary.BigEndian.PutUint32(buf[i:i+4], ram[(addr&0xFFFFFFFC)+uint32(i)])
}
WriteBytes(int(ram[0xc0000010]), buf[offset:offset+len])
//fmt.Printf("write %x %x %x\n", ram[0xc0000010], ram[0xc0000014], ram[0xc0000018])
} else {
//os.Stderr.WriteString(fmt.Sprintf("syscall %d at %x (step %d)\n", syscall, nret, pcCount))
}
}
s.PcCount += 1
s.seenWrite = false
}
return common.BytesToHash(mret)
}
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) RevertToSnapshot(revid int) {}
func (s *StateDB) SetCode(addr common.Address, code []byte) {
fmt.Println("SetCode", addr, len(code))
s.Bytecodes[addr] = code
}
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {}
func (s *StateDB) SetState(fakeaddr common.Address, key, value common.Hash) {
if s.useRealState {
fmt.Println("SetState", fakeaddr, key, value)
s.RealState[key] = value
return
}
//fmt.Println("SetState", addr, key, value)
addr := bytesTo32(key.Bytes())
dat := bytesTo32(value.Bytes())
if addr == 0xc0000080 {
s.seenWrite = true
}
if s.Debug >= 2 {
fmt.Println("HOOKED WRITE! ", fmt.Sprintf("%x = %x (at step %d)", addr, dat, s.PcCount))
}
WriteRam(s.Ram, addr, dat)
}
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
return true, true
}
func (s *StateDB) Snapshot() int { return 0 }
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 }
......@@ -2,12 +2,9 @@ package main
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
......@@ -18,143 +15,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
var debug int = 0
var pcCount int = 0
var ram map[uint32](uint32)
var ministart time.Time
var callback func(int, map[uint32](uint32))
func bytesTo32(a []byte) uint32 {
//return uint32(common.BytesToHash(a).Big().Uint64())
return binary.BigEndian.Uint32(a[28:])
}
type StateDB struct {
Bytecode []byte
}
func (s *StateDB) AddAddressToAccessList(addr common.Address) {}
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {}
func (s *StateDB) AddLog(log *types.Log) {}
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {}
func (s *StateDB) AddRefund(gas uint64) {}
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {}
func (s *StateDB) AddressInAccessList(addr common.Address) bool { return true }
func (s *StateDB) CreateAccount(addr common.Address) {}
func (s *StateDB) Empty(addr common.Address) bool { return false }
func (s *StateDB) Exist(addr common.Address) bool { return true }
func (b *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
return nil
}
func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 }
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 100 }
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
return common.Hash{}
}
func (s *StateDB) GetNonce(addr common.Address) uint64 { return 0 }
func (s *StateDB) GetRefund() uint64 { return 0 }
var seenWrite bool = true
func (s *StateDB) GetState(fakeaddr common.Address, hash common.Hash) common.Hash {
//fmt.Println("GetState", addr, hash)
addr := bytesTo32(hash.Bytes())
nret := ram[addr]
mret := make([]byte, 32)
binary.BigEndian.PutUint32(mret[0x1c:], nret)
if debug >= 2 {
fmt.Println("HOOKED READ! ", fmt.Sprintf("%x = %x", addr, nret))
}
if addr == 0xc0000080 && seenWrite {
if debug >= 1 {
fmt.Printf("%7d %8X %08X : %08X %08X %08X %08X %08X %08X %08X %08X %08X\n",
pcCount, nret&0x7FFFFFFF, ram[nret],
ram[0xc0000004],
ram[0xc0000008], ram[0xc000000c], ram[0xc0000010], ram[0xc0000014],
ram[0xc0000018], ram[0xc000001c], ram[0xc0000020], ram[0xc0000024])
}
if (pcCount % 100000) == 0 {
steps_per_sec := float64(pcCount) * 1e9 / float64(time.Now().Sub(ministart).Nanoseconds())
os.Stderr.WriteString(fmt.Sprintf("%10d pc: %x steps per s %f ram entries %d\n", pcCount, nret&0x7FFFFFFF, steps_per_sec, len(ram)))
}
if callback != nil {
callback(pcCount, ram)
}
if ram[nret] == 0xC {
syscall := ram[0xc0000008]
if syscall == 4020 {
oracle_hash := make([]byte, 0x20)
for i := uint32(0); i < 0x20; i += 4 {
binary.BigEndian.PutUint32(oracle_hash[i:i+4], ram[0x30001000+i])
}
hash := common.BytesToHash(oracle_hash)
key := fmt.Sprintf("/tmp/eth/%s", hash)
value, _ := ioutil.ReadFile(key)
WriteRam(ram, 0x31000000, uint32(len(value)))
value = append(value, 0, 0, 0)
for i := uint32(0); i < ram[0x31000000]; i += 4 {
WriteRam(ram, 0x31000004+i, binary.BigEndian.Uint32(value[i:i+4]))
}
} else if syscall == 4004 {
len := ram[0xc0000018]
buf := make([]byte, len+0x10)
addr := ram[0xc0000014]
offset := addr & 3
for i := uint32(0); i < offset+len; i += 4 {
binary.BigEndian.PutUint32(buf[i:i+4], ram[(addr&0xFFFFFFFC)+uint32(i)])
}
WriteBytes(int(ram[0xc0000010]), buf[offset:offset+len])
//fmt.Printf("write %x %x %x\n", ram[0xc0000010], ram[0xc0000014], ram[0xc0000018])
} else {
//os.Stderr.WriteString(fmt.Sprintf("syscall %d at %x (step %d)\n", syscall, nret, pcCount))
}
}
pcCount += 1
seenWrite = false
}
return common.BytesToHash(mret)
}
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) RevertToSnapshot(revid int) {}
func (s *StateDB) SetCode(addr common.Address, code []byte) {}
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {}
func (s *StateDB) SetState(fakeaddr common.Address, key, value common.Hash) {
//fmt.Println("SetState", addr, key, value)
addr := bytesTo32(key.Bytes())
dat := bytesTo32(value.Bytes())
if addr == 0xc0000080 {
seenWrite = true
}
if debug >= 2 {
fmt.Println("HOOKED WRITE! ", fmt.Sprintf("%x = %x (at step %d)", addr, dat, pcCount))
}
WriteRam(ram, addr, dat)
}
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
return true, true
}
func (s *StateDB) Snapshot() int { return 0 }
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{}
......@@ -183,14 +45,19 @@ type jsoncontract struct {
DeployedBytecode string `json:"deployedBytecode"`
}
func GetInterpreterAndBytecode(ldebug int) (*vm.EVMInterpreter, []byte) {
debug = ldebug
func GetBytecode(deployed bool) []byte {
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:])
if deployed {
return common.Hex2Bytes(jj.DeployedBytecode[2:])
} else {
return common.Hex2Bytes(jj.Bytecode[2:])
}
}
statedb := &StateDB{Bytecode: bytecode}
func GetInterpreter(ldebug int, realState bool) (*vm.EVMInterpreter, *StateDB) {
statedb := NewStateDB(ldebug, realState)
var header types.Header
header.Number = big.NewInt(13284469)
......@@ -208,22 +75,24 @@ func GetInterpreterAndBytecode(ldebug int) (*vm.EVMInterpreter, []byte) {
evm := vm.NewEVM(blockContext, txContext, statedb, params.MainnetChainConfig, config)
interpreter := vm.NewEVMInterpreter(evm, config)
return interpreter, bytecode
return interpreter, statedb
}
func RunWithRam(lram map[uint32](uint32), steps int, debug int, lcallback func(int, map[uint32](uint32))) (uint64, error) {
interpreter, bytecode := GetInterpreterAndBytecode(debug)
interpreter, statedb := GetInterpreter(debug, false)
statedb.Ram = lram
callback = lcallback
ram = lram
gas := 100000 * uint64(steps)
// 0xdb7df598
from := common.Address{}
to := common.HexToAddress("0x1337")
input := []byte{0xdb, 0x7d, 0xf5, 0x98} // Steps(bytes32, uint256)
bytecode := GetBytecode(true)
statedb.Bytecodes[to] = bytecode
input := crypto.Keccak256Hash([]byte("Steps(bytes32,uint256)")).Bytes()[:4]
input = append(input, common.BigToHash(common.Big0).Bytes()...)
input = append(input, common.BigToHash(big.NewInt(int64(steps))).Bytes()...)
......
......@@ -16,13 +16,13 @@ import (
type PreimageKeyValueWriter struct{}
var preimages = make(map[common.Hash][]byte)
var Preimages = make(map[common.Hash][]byte)
func SerializeTrie(root common.Hash) []byte {
b := new(bytes.Buffer)
e := gob.NewEncoder(b)
check(e.Encode(root))
check(e.Encode(preimages))
check(e.Encode(Preimages))
return b.Bytes()
}
......@@ -32,23 +32,23 @@ func (kw PreimageKeyValueWriter) Put(key []byte, value []byte) error {
if hash != common.BytesToHash(key) {
panic("bad preimage value write")
}
preimages[hash] = common.CopyBytes(value)
Preimages[hash] = common.CopyBytes(value)
return nil
}
func (kw PreimageKeyValueWriter) Delete(key []byte) error {
delete(preimages, common.BytesToHash(key))
delete(Preimages, common.BytesToHash(key))
return nil
}
// full nodes / BRANCH_NODE have 17 values, each a hash
// LEAF or EXTENSION nodes have 2 values, a path and value
func parseNode(node common.Hash, depth int) {
func ParseNode(node common.Hash, depth int) {
if depth > 3 {
return
}
sprefix := strings.Repeat(" ", depth)
buf := preimages[node]
buf := Preimages[node]
elems, _, err := rlp.SplitList(buf)
check(err)
c, _ := rlp.CountValues(elems)
......@@ -61,8 +61,8 @@ func parseNode(node common.Hash, depth int) {
fmt.Println(sprefix, i, kind, val, len(val))
if len(val) == 32 {
hh := common.BytesToHash(val)
fmt.Println(sprefix, "node found with len", len(preimages[hh]))
parseNode(hh, depth+1)
fmt.Println(sprefix, "node found with len", len(Preimages[hh]))
ParseNode(hh, depth+1)
}
}
}
......@@ -90,8 +90,8 @@ func RamToTrie(ram map[uint32](uint32)) common.Hash {
mt.Update(tk, tv)
}
mt.Commit()
fmt.Println("ram hash", mt.Hash())
fmt.Println("hash count", len(preimages))
parseNode(mt.Hash(), 0)
/*fmt.Println("ram hash", mt.Hash())
fmt.Println("hash count", len(Preimages))
parseNode(mt.Hash(), 0)*/
return mt.Hash()
}
......@@ -9,18 +9,12 @@ import (
// go test -run TestTrie
func TestTrie(t *testing.T) {
fn := "../mipigo/test/test.bin"
ram := make(map[uint32](uint32))
// TODO: copied from compare_test.go
LoadMappedFile(fn, ram, 0)
/*inputFile := fmt.Sprintf("/tmp/eth/%d", 13284469)
LoadMappedFile(inputFile, ram, 0xB0000000)*/
for i := uint32(0xC0000000); i < 0xC0000000+36*4; i += 4 {
WriteRam(ram, i, 0)
}
LoadMappedFile("../mipigo/test/test.bin", ram, 0)
ZeroRegisters(ram)
root := RamToTrie(ram)
ParseNode(root, 0)
dat := SerializeTrie(root)
fmt.Println("serialized length is", len(dat))
ioutil.WriteFile("/tmp/eth/ramtrie", dat, 0644)
......
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