rework vm

This commit is contained in:
obscuren 2014-11-30 21:02:59 +01:00
parent 1bce02eff7
commit 616066a598
3 changed files with 142 additions and 146 deletions

@ -5,11 +5,10 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
)
type Environment interface {
State() *state.State
//State() *state.State
Origin() []byte
BlockNumber() *big.Int
@ -19,8 +18,16 @@ type Environment interface {
Difficulty() *big.Int
BlockHash() []byte
GasLimit() *big.Int
Transfer(from, to Account, amount *big.Int) error
AddLog(*state.Log)
AddLog(addr []byte, topics [][]byte, data []byte)
DeleteAccount(addr []byte)
SetState(addr, key, value []byte)
GetState(addr, key []byte) []byte
Balance(addr []byte) *big.Int
AddBalance(addr []byte, balance *big.Int)
GetCode(addr []byte) []byte
Refund(addr []byte, gas, price *big.Int)
}
type Object interface {
@ -43,9 +50,5 @@ func Transfer(from, to Account, amount *big.Int) error {
from.SubBalance(amount)
to.AddBalance(amount)
// Add default LOG. Default = big(sender.addr) + 1
//addr := ethutil.BigD(receiver.Address())
//tx.addLog(vm.Log{sender.Address(), [][]byte{ethutil.U256(addr.Add(addr, ethutil.Big1)).Bytes()}, nil})
return nil
}

@ -90,6 +90,6 @@ func (self *Execution) exec(code, caddr []byte, caller ClosureRef) (ret []byte,
return
}
func (self *Execution) Create(caller ClosureRef) (ret []byte, err error) {
func (self *Execution) Create(caller []byte) (ret []byte, err error) {
return self.exec(self.input, nil, caller)
}

@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
)
type DebugVm struct {
@ -29,6 +28,13 @@ type DebugVm struct {
depth int
}
type Options struct {
Address, Caller []byte
Data []byte
Code []byte
Value, Gas, Price *big.Int
}
func NewDebugVm(env Environment) *DebugVm {
lt := LogTyPretty
if ethutil.Config.Diff {
@ -38,7 +44,13 @@ func NewDebugVm(env Environment) *DebugVm {
return &DebugVm{env: env, logTy: lt, Recoverable: true}
}
func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
//func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
func (self *DebugVm) Run(call Options) (ret []byte, gas *big.Int, err error) {
// Don't bother with the execution if there's no code.
if len(call.Code) == 0 {
return nil, new(big.Int), nil
}
self.depth++
if self.Recoverable {
@ -47,41 +59,49 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
if r := recover(); r != nil {
self.Endl()
closure.UseGas(closure.Gas)
ret = closure.Return(nil)
gas = new(big.Int)
err = fmt.Errorf("%v", r)
}
}()
}
gas = new(big.Int).Set(opt.Gas)
var (
op OpCode
destinations = analyseJumpDests(closure.Code)
destinations = analyseJumpDests(call.Code)
mem = NewMemory()
stack = NewStack()
pc = big.NewInt(0)
step = 0
prevStep = 0
statedb = self.env.State()
//statedb = self.env.State()
require = func(m int) {
if stack.Len() < m {
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
}
}
useGas = func(amount *big.Int) bool {
if amount.Cmp(gas) > 0 {
return false
}
gas.Sub(gas, amount)
return true
}
jump = func(from, to *big.Int) {
p := int(to.Int64())
p := to.Uint64()
self.Printf(" ~> %v", to)
// Return to start
if p == 0 {
pc = big.NewInt(0)
} else {
nop := OpCode(closure.GetOp(p))
nop := OpCode(call.GetOp(p))
if !(nop == JUMPDEST || destinations[from.Int64()] != nil) {
panic(fmt.Sprintf("JUMP missed JUMPDEST (%v) %v", nop, p))
} else if nop == JUMP || nop == JUMPI {
@ -96,17 +116,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
}
)
// Debug hook
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
// Don't bother with the execution if there's no code.
if len(closure.Code) == 0 {
return closure.Return(nil), nil
}
vmlogger.Debugf("(%d) %x gas: %v (d) %x\n", self.depth, closure.Address(), closure.Gas, closure.Args)
vmlogger.Debugf("(%d) %x gas: %v (d) %x\n", self.depth, call.Address, gas, call.Data)
for {
prevStep = step
@ -115,10 +125,11 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
step++
// Get the memory location of pc
op = closure.GetOp(int(pc.Uint64()))
op = call.GetOp(pc.Uint64())
// XXX Leave this Println intact. Don't change this to the log system.
// Used for creating diffs between implementations
/*
if self.logTy == LogTyDiff {
switch op {
case STOP, RETURN, SUICIDE:
@ -135,11 +146,12 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
}
*/
gas := new(big.Int)
reqGas := new(big.Int)
addStepGasUsage := func(amount *big.Int) {
if amount.Cmp(ethutil.Big0) >= 0 {
gas.Add(gas, amount)
reqGas.Add(reqGas, amount)
}
}
@ -166,42 +178,43 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
require(n + 2)
mSize, mStart := stack.Peekn()
gas.Set(GasLog)
reqGs.Set(GasLog)
addStepGasUsage(new(big.Int).Mul(big.NewInt(int64(n)), GasLog))
addStepGasUsage(new(big.Int).Add(mSize, mStart))
// Gas only
case STOP:
gas.Set(ethutil.Big0)
reqGas.Set(ethutil.Big0)
case SUICIDE:
require(1)
gas.Set(ethutil.Big0)
reqGas.Set(ethutil.Big0)
case SLOAD:
require(1)
gas.Set(GasSLoad)
reqGas.Set(GasSLoad)
// Memory resize & Gas
case SSTORE:
require(2)
var mult *big.Int
y, x := stack.Peekn()
val := closure.GetStorage(x)
val := ethutil.BigD(self.env.GetState(x.Bytes())) //closure.GetStorage(x)
if val.BigInt().Cmp(ethutil.Big0) == 0 && len(y.Bytes()) > 0 {
// 0 => non 0
mult = ethutil.Big3
} else if val.BigInt().Cmp(ethutil.Big0) != 0 && len(y.Bytes()) == 0 {
statedb.Refund(closure.caller.Address(), GasSStoreRefund, closure.Price)
//statedb.Refund(closure.caller.Address(), GasSStoreRefund, closure.Price)
self.env.Refund(call.Caller, GasSStoreRefund, call.Price)
mult = ethutil.Big0
} else {
// non 0 => non 0
mult = ethutil.Big1
}
gas.Set(new(big.Int).Mul(mult, GasSStore))
reqGas.Set(new(big.Int).Mul(mult, GasSStore))
case BALANCE:
require(1)
gas.Set(GasBalance)
reqGas.Set(GasBalance)
case MSTORE:
require(2)
newMemSize = calcMemSize(stack.Peek(), u256(32))
@ -219,7 +232,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
case SHA3:
require(2)
gas.Set(GasSha)
reqGas.Set(GasSha)
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
case CALLDATACOPY:
@ -236,7 +249,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4])
case CALL, CALLCODE:
require(7)
gas.Set(GasCall)
reqGas.Set(GasCall)
addStepGasUsage(stack.data[stack.Len()-1])
x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7])
@ -245,7 +258,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
newMemSize = ethutil.BigMax(x, y)
case CREATE:
require(3)
gas.Set(GasCreate)
reqGas.Set(GasCreate)
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3])
}
@ -255,6 +268,12 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
newMemSize.Div(newMemSize, u256(32))
newMemSize.Mul(newMemSize, u256(32))
switch op {
// Additional gas usage on *CODPY
case CALLDATACOPY, CODECOPY, EXTCODECOPY:
addStepGasUsage(new(big.Int).Div(newMemSize, u256(32)))
}
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len())))
memGasUsage.Mul(GasMemory, memGasUsage)
@ -268,16 +287,12 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
}
self.Printf("(pc) %-3d -o- %-14s", pc, op.String())
self.Printf(" (m) %-4d (s) %-4d (g) %-3v (%v)", mem.Len(), stack.Len(), gas, closure.Gas)
self.Printf(" (m) %-4d (s) %-4d (g) %-3v (%v)", mem.Len(), stack.Len(), reqGas, gas)
if !closure.UseGas(gas) {
if !useGas(regGas) {
self.Endl()
tmp := new(big.Int).Set(closure.Gas)
closure.UseGas(closure.Gas)
return closure.Return(nil), OOG(gas, tmp)
return nil, new(big.Int), OOG(reqGas, gas)
}
switch op {
@ -553,13 +568,15 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => %x", data)
// 0x30 range
case ADDRESS:
stack.Push(ethutil.BigD(closure.Address()))
//stack.Push(ethutil.BigD(closure.Address()))
stack.Push(ethutil.BigD(call.Address))
self.Printf(" => %x", closure.Address())
self.Printf(" => %x", call.Address)
case BALANCE:
addr := stack.Pop().Bytes()
balance := statedb.GetBalance(addr)
//balance := statedb.GetBalance(addr)
balance := self.env.GetBalance(addr)
stack.Push(balance)
@ -571,41 +588,42 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => %x", origin)
case CALLER:
caller := closure.caller.Address()
stack.Push(ethutil.BigD(caller))
//caller := closure.caller.Address()
//stack.Push(ethutil.BigD(caller))
stack.Push(call.Caller)
self.Printf(" => %x", caller)
self.Printf(" => %x", call.Caller)
case CALLVALUE:
value := closure.exe.value
//value := closure.exe.value
stack.Push(value)
stack.Push(call.Value)
self.Printf(" => %v", value)
self.Printf(" => %v", call.Value)
case CALLDATALOAD:
var (
offset = stack.Pop()
data = make([]byte, 32)
lenData = big.NewInt(int64(len(closure.Args)))
lenData = big.NewInt(int64(len(call.Data)))
)
if lenData.Cmp(offset) >= 0 {
length := new(big.Int).Add(offset, ethutil.Big32)
length = ethutil.BigMin(length, lenData)
copy(data, closure.Args[offset.Int64():length.Int64()])
copy(data, call.Data[offset.Int64():length.Int64()])
}
self.Printf(" => 0x%x", data)
stack.Push(ethutil.BigD(data))
case CALLDATASIZE:
l := int64(len(closure.Args))
l := int64(len(call.Data))
stack.Push(big.NewInt(l))
self.Printf(" => %d", l)
case CALLDATACOPY:
var (
size = int64(len(closure.Args))
size = int64(len(call.Data))
mOff = stack.Pop().Int64()
cOff = stack.Pop().Int64()
l = stack.Pop().Int64()
@ -618,7 +636,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
l = 0
}
code := closure.Args[cOff : cOff+l]
code := call.Data[cOff : cOff+l]
mem.Set(mOff, l, code)
@ -628,9 +646,9 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
if op == EXTCODESIZE {
addr := stack.Pop().Bytes()
code = statedb.GetCode(addr)
self.env.GetCode(addr)
} else {
code = closure.Code
code = call.Code
}
l := big.NewInt(int64(len(code)))
@ -642,9 +660,9 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
if op == EXTCODECOPY {
addr := stack.Pop().Bytes()
code = statedb.GetCode(addr)
code = self.env.GetCode(addr)
} else {
code = closure.Code
code = call.Code
}
var (
@ -667,9 +685,9 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, code[cOff:cOff+l])
case GASPRICE:
stack.Push(closure.Price)
stack.Push(call.Price)
self.Printf(" => %v", closure.Price)
self.Printf(" => %v", call.Price)
// 0x40 range
case PREVHASH:
@ -707,17 +725,17 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
// 0x50 range
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
a := big.NewInt(int64(op) - int64(PUSH1) + 1)
a := uint64(op) - uint64(PUSH1) + 1
pc.Add(pc, ethutil.Big1)
data := closure.Gets(pc, a)
val := ethutil.BigD(data.Bytes())
data := call.Get(pc.Uint64(), a) //closure.Gets(pc, a)
val := ethutil.BigD(data)
// Push value to stack
stack.Push(val)
pc.Add(pc, a.Sub(a, big.NewInt(1)))
pc.Add(pc, big.NewInt(int64(a)-1))
step += int(op) - int(PUSH1) + 1
step += uint64(op) - uint64(PUSH1) + 1
self.Printf(" => 0x%x", data.Bytes())
self.Printf(" => 0x%x", data)
case POP:
stack.Pop()
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
@ -725,10 +743,6 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
v := stack.Dupn(n)
self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes())
if OpCode(closure.Get(new(big.Int).Add(pc, ethutil.Big1)).Uint()) == POP && OpCode(closure.Get(new(big.Int).Add(pc, big.NewInt(2))).Uint()) == POP {
fmt.Println(toValue(v))
}
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2)
x, y := stack.Swapn(n)
@ -743,8 +757,8 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
topics[i] = stack.Pop().Bytes()
}
log := &state.Log{closure.Address(), topics, data}
self.env.AddLog(log)
//log := &state.Log{closure.Address(), topics, data}
self.env.AddLog(call.Address, topics, data)
self.Printf(" => %v", log)
case MLOAD:
@ -768,18 +782,16 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => [%v] 0x%x", off, val)
case SLOAD:
loc := stack.Pop()
val := ethutil.BigD(statedb.GetState(closure.Address(), loc.Bytes()))
val := ethutil.BigD(self.env.GetState(call.Address, loc.Bytes()))
stack.Push(val)
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
case SSTORE:
val, loc := stack.Popn()
statedb.SetState(closure.Address(), loc.Bytes(), val)
self.env.SetState(call.Address, loc.Bytes(), val.Bytes())
//statedb.SetState(closure.Address(), loc.Bytes(), val)
// Debug sessions are allowed to run without message
if closure.message != nil {
closure.message.AddStorageChange(loc.Bytes())
}
//closure.message.AddStorageChange(loc.Bytes())
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
case JUMP:
@ -802,7 +814,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
case MSIZE:
stack.Push(big.NewInt(int64(mem.Len())))
case GAS:
stack.Push(closure.Gas)
stack.Push(call.Gas)
// 0x60 range
case CREATE:
var (
@ -810,7 +822,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
value = stack.Pop()
size, offset = stack.Popn()
input = mem.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(closure.Gas)
gas = new(big.Int).Set(call.Gas)
// Snapshot the current stack so we are able to
// revert back to it later.
@ -818,16 +830,19 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
)
// Generate a new address
n := statedb.GetNonce(closure.Address())
addr := crypto.CreateAddress(closure.Address(), n)
statedb.SetNonce(closure.Address(), n+1)
//n := statedb.GetNonce(closure.Address())
//addr := crypto.CreateAddress(closure.Address(), n)
//statedb.SetNonce(closure.Address(), n+1)
n := self.env.GetNonce(call.Address)
addr := crypto.CreateAddress(call.Address, n)
self.env.SetNonce(call.Address, n+1)
self.Printf(" (*) %x", addr).Endl()
closure.UseGas(closure.Gas)
//closure.UseGas(closure.Gas)
msg := NewExecution(self, addr, input, gas, closure.Price, value)
ret, err := msg.Create(closure)
msg := NewExecution(self, addr, input, gas, call.Price, value)
ret, lgas, err := msg.Create(call.Address)
if err != nil {
stack.Push(ethutil.BigFalse)
@ -841,12 +856,9 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Push(ethutil.BigD(addr))
}
self.Endl()
gas = lgas
// Debug hook
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
self.Endl()
case CALL, CALLCODE:
self.Endl()
@ -863,12 +875,12 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
var executeAddr []byte
if op == CALLCODE {
executeAddr = closure.Address()
executeAddr = call.Address //closure.Address()
} else {
executeAddr = addr.Bytes()
}
msg := NewExecution(self, executeAddr, args, gas, closure.Price, value)
msg := NewExecution(self, executeAddr, args, gas, call.Price, value)
ret, err := msg.Exec(addr.Bytes(), closure)
if err != nil {
stack.Push(ethutil.BigFalse)
@ -881,24 +893,22 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
}
self.Printf("resume %x", closure.Address())
// Debug hook
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
case RETURN:
size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64())
self.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
return closure.Return(ret), nil
return ret, gas, nil
//return closure.Return(ret), gas, nil
case SUICIDE:
//receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes())
//receiver.AddAmount(statedb.GetBalance(closure.Address()))
//statedb.Delete(closure.Address())
receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes())
receiver.AddAmount(statedb.GetBalance(closure.Address()))
statedb.Delete(closure.Address())
self.env.AddBalance(stack.Pop().Bytes(), self.env.Balance(call.Address))
self.env.DeleteAccount(call.Address)
fallthrough
case STOP: // Stop the closure
@ -917,23 +927,6 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
pc.Add(pc, ethutil.Big1)
self.Endl()
if self.Dbg != nil {
for _, instrNo := range self.Dbg.BreakPoints() {
if pc.Cmp(big.NewInt(instrNo)) == 0 {
self.Stepping = true
if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(closure.Address())) {
return nil, nil
}
} else if self.Stepping {
if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(closure.Address())) {
return nil, nil
}
}
}
}
}
}