aboutsummaryrefslogtreecommitdiffstats
path: root/ethvm
diff options
context:
space:
mode:
Diffstat (limited to 'ethvm')
-rw-r--r--ethvm/closure.go1
-rw-r--r--ethvm/stack.go6
-rw-r--r--ethvm/types.go47
-rw-r--r--ethvm/vm.go331
4 files changed, 251 insertions, 134 deletions
diff --git a/ethvm/closure.go b/ethvm/closure.go
index 54bfd05f4..c047a83b7 100644
--- a/ethvm/closure.go
+++ b/ethvm/closure.go
@@ -12,6 +12,7 @@ import (
type ClosureRef interface {
ReturnGas(*big.Int, *big.Int)
Address() []byte
+ Object() *ethstate.StateObject
GetStorage(*big.Int) *ethutil.Value
SetStorage(*big.Int, *ethutil.Value)
}
diff --git a/ethvm/stack.go b/ethvm/stack.go
index 82dd612c2..4ac023fb9 100644
--- a/ethvm/stack.go
+++ b/ethvm/stack.go
@@ -65,13 +65,13 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) {
}
func (st *Stack) Swapn(n int) (*big.Int, *big.Int) {
- st.data[n], st.data[0] = st.data[0], st.data[n]
+ st.data[len(st.data)-n], st.data[len(st.data)-1] = st.data[len(st.data)-1], st.data[len(st.data)-n]
- return st.data[n], st.data[0]
+ return st.data[len(st.data)-n], st.data[len(st.data)-1]
}
func (st *Stack) Dupn(n int) *big.Int {
- st.Push(st.data[n])
+ st.Push(st.data[len(st.data)-n])
return st.Peek()
}
diff --git a/ethvm/types.go b/ethvm/types.go
index 36ba395d6..9cddd7c33 100644
--- a/ethvm/types.go
+++ b/ethvm/types.go
@@ -49,6 +49,8 @@ const (
CODESIZE = 0x38
CODECOPY = 0x39
GASPRICE = 0x3a
+ EXTCODECOPY = 0x3b
+ EXTCODESIZE = 0x3c
// 0x40 range - block operations
PREVHASH = 0x40
@@ -142,9 +144,11 @@ const (
SWAP16 = 0x9f
// 0xf0 range - closures
- CREATE = 0xf0
- CALL = 0xf1
- RETURN = 0xf2
+ CREATE = 0xf0
+ CALL = 0xf1
+ RETURN = 0xf2
+ POST = 0xf3
+ CALLSTATELESS = 0xf4
// 0x70 range - other
LOG = 0xfe // XXX Unofficial
@@ -196,12 +200,14 @@ var opCodeToString = map[OpCode]string{
GASPRICE: "TXGASPRICE",
// 0x40 range - block operations
- PREVHASH: "PREVHASH",
- COINBASE: "COINBASE",
- TIMESTAMP: "TIMESTAMP",
- NUMBER: "NUMBER",
- DIFFICULTY: "DIFFICULTY",
- GASLIMIT: "GASLIMIT",
+ PREVHASH: "PREVHASH",
+ COINBASE: "COINBASE",
+ TIMESTAMP: "TIMESTAMP",
+ NUMBER: "NUMBER",
+ DIFFICULTY: "DIFFICULTY",
+ GASLIMIT: "GASLIMIT",
+ EXTCODESIZE: "EXTCODESIZE",
+ EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution
POP: "POP",
@@ -287,9 +293,11 @@ var opCodeToString = map[OpCode]string{
SWAP16: "SWAP16",
// 0xf0 range
- CREATE: "CREATE",
- CALL: "CALL",
- RETURN: "RETURN",
+ CREATE: "CREATE",
+ CALL: "CALL",
+ RETURN: "RETURN",
+ POST: "POST",
+ CALLSTATELESS: "CALLSTATELESS",
// 0x70 range - other
LOG: "LOG",
@@ -342,7 +350,12 @@ var OpCodes = map[string]byte{
"CALLVALUE": 0x34,
"CALLDATALOAD": 0x35,
"CALLDATASIZE": 0x36,
- "GASPRICE": 0x38,
+ "CALLDATACOPY": 0x37,
+ "CODESIZE": 0x38,
+ "CODECOPY": 0x39,
+ "GASPRICE": 0x3a,
+ "EXTCODECOPY": 0x3b,
+ "EXTCODESIZE": 0x3c,
// 0x40 range - block operations
"PREVHASH": 0x40,
@@ -435,9 +448,11 @@ var OpCodes = map[string]byte{
"SWAP16": 0x9f,
// 0xf0 range - closures
- "CREATE": 0xf0,
- "CALL": 0xf1,
- "RETURN": 0xf2,
+ "CREATE": 0xf0,
+ "CALL": 0xf1,
+ "RETURN": 0xf2,
+ "POST": 0xf3,
+ "CALLSTATELESS": 0xf4,
// 0x70 range - other
"LOG": 0xfe,
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
+}