aboutsummaryrefslogtreecommitdiffstats
path: root/ethvm/vm.go
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-09-23 01:34:31 +0800
committerobscuren <geffobscura@gmail.com>2014-09-23 01:34:31 +0800
commitce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9 (patch)
treeac183aa3312a4589cb3e4995cc103cfa58ef95da /ethvm/vm.go
parent8ef17c2fb138ae254a0cc7ac509a7ab1177ee4ac (diff)
parent7d08e4f7d14600ee4ed38fc9d435e9c2e0e0fdac (diff)
downloaddexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar.gz
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar.bz2
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar.lz
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar.xz
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.tar.zst
dexon-ce149d2733bd55e8e9b16dd4b60b6bad17c3d7d9.zip
Merge branch 'release/0.6.5'
Diffstat (limited to 'ethvm/vm.go')
-rw-r--r--ethvm/vm.go331
1 files changed, 216 insertions, 115 deletions
diff --git a/ethvm/vm.go b/ethvm/vm.go
index 873a80c44..7aff320f9 100644
--- a/ethvm/vm.go
+++ b/ethvm/vm.go
@@ -1,8 +1,8 @@
package ethvm
import (
+ "container/list"
"fmt"
- "math"
"math/big"
"github.com/ethereum/eth-go/ethcrypto"
@@ -18,11 +18,6 @@ type Debugger interface {
}
type Vm struct {
- // Stack for processing contracts
- stack *Stack
- // non-persistent key/value memory storage
- mem map[string]*big.Int
-
env Environment
Verbose bool
@@ -40,6 +35,8 @@ type Vm struct {
Fn string
Recoverable bool
+
+ queue *list.List
}
type Environment interface {
@@ -66,7 +63,20 @@ func New(env Environment) *Vm {
lt = LogTyDiff
}
- return &Vm{env: env, logTy: lt, Recoverable: true}
+ return &Vm{env: env, logTy: lt, Recoverable: true, queue: list.New()}
+}
+
+func calcMemSize(off, l *big.Int) *big.Int {
+ if l.Cmp(ethutil.Big0) == 0 {
+ return ethutil.Big0
+ }
+
+ return new(big.Int).Add(off, l)
+}
+
+// Simple helper
+func u256(n int64) *big.Int {
+ return big.NewInt(n)
}
func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
@@ -122,15 +132,13 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// 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:
- closure.object.EachStorage(func(key string, value *ethutil.Value) {
- value.Decode()
- fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
- })
- }
- */
+ switch op {
+ case STOP, RETURN, SUICIDE:
+ closure.object.EachStorage(func(key string, value *ethutil.Value) {
+ value.Decode()
+ fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
+ })
+ }
b := pc.Bytes()
if len(b) == 0 {
@@ -149,7 +157,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
addStepGasUsage(GasStep)
- var newMemSize uint64 = 0
+ var newMemSize *big.Int = ethutil.Big0
switch op {
case STOP:
gas.Set(ethutil.Big0)
@@ -173,52 +181,64 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
gas.Set(GasBalance)
case MSTORE:
require(2)
- newMemSize = stack.Peek().Uint64() + 32
+ newMemSize = calcMemSize(stack.Peek(), u256(32))
case MLOAD:
require(1)
- newMemSize = stack.Peek().Uint64() + 32
+ newMemSize = calcMemSize(stack.Peek(), u256(32))
case MSTORE8:
require(2)
- newMemSize = stack.Peek().Uint64() + 1
+ newMemSize = calcMemSize(stack.Peek(), u256(1))
case RETURN:
require(2)
- newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
+ newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
case SHA3:
require(2)
gas.Set(GasSha)
- newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
+ newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
case CALLDATACOPY:
- require(3)
+ require(2)
- newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
+ newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
case CODECOPY:
require(3)
- newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
- case CALL:
+ newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
+ case EXTCODECOPY:
+ require(4)
+
+ newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4])
+ case CALL, CALLSTATELESS:
require(7)
gas.Set(GasCall)
addStepGasUsage(stack.data[stack.Len()-1])
- x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
- y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
+ x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7])
+ y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5])
- newMemSize = uint64(math.Max(float64(x), float64(y)))
+ newMemSize = ethutil.BigMax(x, y)
case CREATE:
require(3)
gas.Set(GasCreate)
- newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
+ newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3])
}
- newMemSize = (newMemSize + 31) / 32 * 32
- if newMemSize > uint64(mem.Len()) {
- m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
- addStepGasUsage(big.NewInt(int64(m)))
+ if newMemSize.Cmp(ethutil.Big0) > 0 {
+ newMemSize.Add(newMemSize, u256(31))
+ newMemSize.Div(newMemSize, u256(32))
+ newMemSize.Mul(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)
+ memGasUsage.Div(memGasUsage, u256(32))
+
+ addStepGasUsage(memGasUsage)
+ }
}
if !closure.UseGas(gas) {
@@ -232,7 +252,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf("(pc) %-3d -o- %-14s", pc, op.String())
self.Printf(" (g) %-3v (%v)", gas, closure.Gas)
- mem.Resize(newMemSize)
+ mem.Resize(newMemSize.Uint64())
switch op {
case LOG:
@@ -551,14 +571,32 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
code := closure.Args[cOff : cOff+l]
mem.Set(mOff, l, code)
- case CODESIZE:
- l := big.NewInt(int64(len(closure.Code)))
+ case CODESIZE, EXTCODESIZE:
+ var code []byte
+ if op == EXTCODECOPY {
+ addr := stack.Pop().Bytes()
+
+ code = self.env.State().GetCode(addr)
+ } else {
+ code = closure.Code
+ }
+
+ l := big.NewInt(int64(len(code)))
stack.Push(l)
self.Printf(" => %d", l)
- case CODECOPY:
+ case CODECOPY, EXTCODECOPY:
+ var code []byte
+ if op == EXTCODECOPY {
+ addr := stack.Pop().Bytes()
+
+ code = self.env.State().GetCode(addr)
+ } else {
+ code = closure.Code
+ }
+
var (
- size = int64(len(closure.Code))
+ size = int64(len(code))
mOff = stack.Pop().Int64()
cOff = stack.Pop().Int64()
l = stack.Pop().Int64()
@@ -571,9 +609,9 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
l = 0
}
- code := closure.Code[cOff : cOff+l]
+ codeCopy := code[cOff : cOff+l]
- mem.Set(mOff, l, code)
+ mem.Set(mOff, l, codeCopy)
case GASPRICE:
stack.Push(closure.Price)
@@ -632,11 +670,15 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Pop()
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1)
- stack.Dupn(n)
+ 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 + 1)
+ n := int(op - SWAP1 + 2)
x, y := stack.Swapn(n)
self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes())
@@ -656,12 +698,12 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => 0x%x", val)
case MSTORE8:
require(2)
- val, mStart := stack.Popn()
- //base.And(val, new(big.Int).SetInt64(0xff))
- //mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
- mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
+ off := stack.Pop()
+ val := stack.Pop()
- self.Printf(" => 0x%x", val)
+ mem.store[off.Int64()] = byte(val.Int64() & 0xff)
+
+ self.Printf(" => [%v] 0x%x", off, val)
case SLOAD:
require(1)
loc := stack.Pop()
@@ -711,6 +753,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
err error
value = stack.Pop()
size, offset = stack.Popn()
+ input = mem.Get(offset.Int64(), size.Int64())
+ gas = new(big.Int).Set(closure.Gas)
// Snapshot the current stack so we are able to
// revert back to it later.
@@ -726,37 +770,10 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" (*) %x", addr).Endl()
- msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
- To: addr, From: closure.Address(),
- Origin: self.env.Origin(),
- Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
- Value: value,
- })
-
- // Create a new contract
- contract := self.env.State().NewStateObject(addr)
- if contract.Balance.Cmp(value) >= 0 {
- closure.object.SubAmount(value)
- contract.AddAmount(value)
-
- // Set the init script
- initCode := mem.Get(offset.Int64(), size.Int64())
- msg.Input = initCode
-
- // Transfer all remaining gas to the new
- // contract so it may run the init script
- gas := new(big.Int).Set(closure.Gas)
- closure.UseGas(closure.Gas)
-
- // Create the closure
- c := NewClosure(msg, closure, contract, initCode, gas, closure.Price)
- // Call the closure and set the return value as
- // main script.
- contract.Code, _, err = c.Call(self, nil)
- } else {
- err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
- }
+ closure.UseGas(closure.Gas)
+ msg := NewMessage(self, addr, input, gas, closure.Price, value)
+ ret, err := msg.Exec(addr, closure)
if err != nil {
stack.Push(ethutil.BigFalse)
@@ -765,17 +782,18 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf("CREATE err %v", err)
} else {
- stack.Push(ethutil.BigD(addr))
+ msg.object.Code = ret
- msg.Output = contract.Code
+ stack.Push(ethutil.BigD(addr))
}
+
self.Endl()
// Debug hook
if self.Dbg != nil {
self.Dbg.SetCode(closure.Code)
}
- case CALL:
+ case CALL, CALLSTATELESS:
require(7)
self.Endl()
@@ -791,51 +809,48 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
- msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
- To: addr.Bytes(), From: closure.Address(),
- Input: args,
- Origin: self.env.Origin(),
- Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
- Value: value,
- })
-
- if closure.object.Balance.Cmp(value) < 0 {
- vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
+ snapshot := self.env.State().Copy()
- closure.ReturnGas(gas, nil)
+ var executeAddr []byte
+ if op == CALLSTATELESS {
+ executeAddr = closure.Address()
+ } else {
+ executeAddr = addr.Bytes()
+ }
+ msg := NewMessage(self, executeAddr, args, gas, closure.Price, value)
+ ret, err := msg.Exec(addr.Bytes(), closure)
+ if err != nil {
stack.Push(ethutil.BigFalse)
- } else {
- snapshot := self.env.State().Copy()
- stateObject := self.env.State().GetOrNewStateObject(addr.Bytes())
+ self.env.State().Set(snapshot)
+ } else {
+ stack.Push(ethutil.BigTrue)
- closure.object.SubAmount(value)
- stateObject.AddAmount(value)
+ mem.Set(retOffset.Int64(), retSize.Int64(), ret)
+ }
- // Create a new callable closure
- c := NewClosure(msg, closure, stateObject, stateObject.Code, gas, closure.Price)
- // Executer the closure and get the return value (if any)
- ret, _, err := c.Call(self, args)
- if err != nil {
- stack.Push(ethutil.BigFalse)
+ // Debug hook
+ if self.Dbg != nil {
+ self.Dbg.SetCode(closure.Code)
+ }
- vmlogger.Debugf("Closure execution failed. %v\n", err)
+ case POST:
+ require(5)
- self.env.State().Set(snapshot)
- } else {
- stack.Push(ethutil.BigTrue)
+ self.Endl()
- mem.Set(retOffset.Int64(), retSize.Int64(), ret)
- }
+ gas := stack.Pop()
+ // Pop gas and value of the stack.
+ value, addr := stack.Popn()
+ // Pop input size and offset
+ inSize, inOffset := stack.Popn()
+ // Get the arguments from the memory
+ args := mem.Get(inOffset.Int64(), inSize.Int64())
- msg.Output = ret
+ msg := NewMessage(self, addr.Bytes(), args, gas, closure.Price, value)
- // Debug hook
- if self.Dbg != nil {
- self.Dbg.SetCode(closure.Code)
- }
- }
+ msg.Postpone()
case RETURN:
require(2)
size, offset := stack.Popn()
@@ -861,6 +876,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
default:
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
+ //panic(fmt.Sprintf("Invalid opcode %x", op))
+
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
}
@@ -887,6 +904,10 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
}
}
+func (self *Vm) Queue() *list.List {
+ return self.queue
+}
+
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose && self.logTy == LogTyPretty {
self.logStr += fmt.Sprintf(format, v...)
@@ -918,3 +939,83 @@ func ensure256(x *big.Int) {
x.SetInt64(0)
}
}
+
+type Message struct {
+ vm *Vm
+ closure *Closure
+ address, input []byte
+ gas, price, value *big.Int
+ object *ethstate.StateObject
+}
+
+func NewMessage(vm *Vm, address, input []byte, gas, gasPrice, value *big.Int) *Message {
+ return &Message{vm: vm, address: address, input: input, gas: gas, price: gasPrice, value: value}
+}
+
+func (self *Message) Postpone() {
+ self.vm.queue.PushBack(self)
+}
+
+func (self *Message) Addr() []byte {
+ return self.address
+}
+
+func (self *Message) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err error) {
+ fmt.Printf("%x %x\n", codeAddr[0:4], self.address[0:4])
+ queue := self.vm.queue
+ self.vm.queue = list.New()
+
+ defer func() {
+ if err == nil {
+ queue.PushBackList(self.vm.queue)
+ }
+
+ self.vm.queue = queue
+ }()
+
+ msg := self.vm.env.State().Manifest().AddMessage(&ethstate.Message{
+ To: self.address, From: caller.Address(),
+ Input: self.input,
+ Origin: self.vm.env.Origin(),
+ Block: self.vm.env.BlockHash(), Timestamp: self.vm.env.Time(), Coinbase: self.vm.env.Coinbase(), Number: self.vm.env.BlockNumber(),
+ Value: self.value,
+ })
+
+ object := caller.Object()
+ if object.Balance.Cmp(self.value) < 0 {
+ caller.ReturnGas(self.gas, self.price)
+
+ err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, object.Balance)
+ } else {
+ stateObject := self.vm.env.State().GetOrNewStateObject(self.address)
+ self.object = stateObject
+
+ caller.Object().SubAmount(self.value)
+ stateObject.AddAmount(self.value)
+
+ // Retrieve the executing code
+ code := self.vm.env.State().GetCode(codeAddr)
+
+ // Create a new callable closure
+ c := NewClosure(msg, caller, stateObject, code, self.gas, self.price)
+ // Executer the closure and get the return value (if any)
+ ret, _, err = c.Call(self.vm, self.input)
+
+ msg.Output = ret
+
+ return ret, err
+ }
+
+ return
+}
+
+// Mainly used for print variables and passing to Print*
+func toValue(val *big.Int) interface{} {
+ // Let's assume a string on right padded zero's
+ b := val.Bytes()
+ if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 {
+ return string(b)
+ }
+
+ return val
+}