Browse Source

core, core/vm, tests: changed the initialisation behaviour of the EVM

The EVM was previously initialised and created for every CALL, CALLCODE,
DELEGATECALL and CREATE. This PR changes this behaviour so that the same
EVM can be used through the session and beyond as long as the
Environment sticks around.
Jeffrey Wilcke 9 years ago
parent
commit
342ae7ce7d

+ 8 - 1
cmd/evm/main.go

@@ -33,6 +33,7 @@ import (
 	"github.com/ethereum/go-ethereum/core/vm"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/logger/glog"
+	"github.com/ethereum/go-ethereum/params"
 )
 
 var (
@@ -174,17 +175,23 @@ type VMEnv struct {
 	Gas   *big.Int
 	time  *big.Int
 	logs  []vm.StructLog
+
+	evm *vm.Vm
 }
 
 func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VMEnv {
-	return &VMEnv{
+	params.HomesteadBlock = new(big.Int)
+	env := &VMEnv{
 		state:      state,
 		transactor: &transactor,
 		value:      value,
 		time:       big.NewInt(time.Now().Unix()),
 	}
+	env.evm = vm.EVM(env)
+	return env
 }
 
+func (self *VMEnv) Vm() *vm.Vm                 { return self.evm }
 func (self *VMEnv) Db() vm.Database            { return self.state }
 func (self *VMEnv) MakeSnapshot() vm.Database  { return self.state.Copy() }
 func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }

+ 2 - 2
core/execution.go

@@ -60,7 +60,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
 }
 
 func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
-	evm := vm.NewVm(env)
+	evm := env.Vm()
 	// Depth check execution. Fail if we're trying to execute above the
 	// limit.
 	if env.Depth() > int(params.CallCreateDepth.Int64()) {
@@ -136,7 +136,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
 }
 
 func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
-	evm := vm.NewVm(env)
+	evm := env.Vm()
 	// Depth check execution. Fail if we're trying to execute above the
 	// limit.
 	if env.Depth() > int(params.CallCreateDepth.Int64()) {

+ 0 - 14
core/vm/common.go

@@ -21,7 +21,6 @@ import (
 	"math/big"
 
 	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/logger/glog"
 	"github.com/ethereum/go-ethereum/params"
 )
 
@@ -51,19 +50,6 @@ var (
 	max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer
 )
 
-// NewVm returns a new VM based on the Environment
-func NewVm(env Environment) VirtualMachine {
-	switch env.VmType() {
-	case JitVmTy:
-		return NewJitVm(env)
-	default:
-		glog.V(0).Infoln("unsupported vm type %d", env.VmType())
-		fallthrough
-	case StdVmTy:
-		return New(env)
-	}
-}
-
 // calculates the memory size required for a step
 func calcMemSize(off, l *big.Int) *big.Int {
 	if l.Cmp(common.Big0) == 0 {

+ 1 - 3
core/vm/environment.go

@@ -58,10 +58,8 @@ type Environment interface {
 	AddStructLog(StructLog)
 	// Returns all coalesced structured logs
 	StructLogs() []StructLog
-
 	// Type of the VM
-	VmType() Type
-
+	Vm() *Vm
 	// Current calling depth
 	Depth() int
 	SetDepth(i int)

+ 0 - 1
core/vm/instructions.go

@@ -597,7 +597,6 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
 	toAddr := common.BigToAddress(to)
 	args := memory.Get(inOffset.Int64(), inSize.Int64())
 	ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
-
 	if err != nil {
 		stack.push(new(big.Int))
 	} else {

+ 6 - 2
core/vm/jit_test.go

@@ -154,7 +154,7 @@ func runVmBench(test vmBench, b *testing.B) {
 		context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
 		context.Code = test.code
 		context.CodeAddr = &common.Address{}
-		_, err := New(env).Run(context, test.input)
+		_, err := env.Vm().Run(context, test.input)
 		if err != nil {
 			b.Error(err)
 			b.FailNow()
@@ -165,12 +165,16 @@ func runVmBench(test vmBench, b *testing.B) {
 type Env struct {
 	gasLimit *big.Int
 	depth    int
+	evm      *Vm
 }
 
 func NewEnv() *Env {
-	return &Env{big.NewInt(10000), 0}
+	env := &Env{gasLimit: big.NewInt(10000), depth: 0}
+	env.evm = EVM(env)
+	return env
 }
 
+func (self *Env) Vm() *Vm                { return self.evm }
 func (self *Env) Origin() common.Address { return common.Address{} }
 func (self *Env) BlockNumber() *big.Int  { return big.NewInt(0) }
 func (self *Env) AddStructLog(log StructLog) {

+ 5 - 7
core/vm/jump_table.go

@@ -13,19 +13,15 @@ type jumpPtr struct {
 
 type vmJumpTable [256]jumpPtr
 
-func (jt vmJumpTable) init(blockNumber *big.Int) {
+func newJumpTable(blockNumber *big.Int) vmJumpTable {
+	var jumpTable vmJumpTable
+
 	// when initialising a new VM execution we must first check the homestead
 	// changes.
 	if params.IsHomestead(blockNumber) {
 		jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
-	} else {
-		jumpTable[DELEGATECALL] = jumpPtr{nil, false}
 	}
-}
 
-var jumpTable vmJumpTable
-
-func init() {
 	jumpTable[ADD] = jumpPtr{opAdd, true}
 	jumpTable[SUB] = jumpPtr{opSub, true}
 	jumpTable[MUL] = jumpPtr{opMul, true}
@@ -156,4 +152,6 @@ func init() {
 	jumpTable[JUMP] = jumpPtr{nil, true}
 	jumpTable[JUMPI] = jumpPtr{nil, true}
 	jumpTable[STOP] = jumpPtr{nil, true}
+
+	return jumpTable
 }

+ 2 - 2
core/vm/jump_table_test.go

@@ -10,13 +10,13 @@ import (
 func TestInit(t *testing.T) {
 	params.HomesteadBlock = big.NewInt(1)
 
-	jumpTable.init(big.NewInt(0))
+	jumpTable := newJumpTable(big.NewInt(0))
 	if jumpTable[DELEGATECALL].valid {
 		t.Error("Expected DELEGATECALL not to be present")
 	}
 
 	for _, n := range []int64{1, 2, 100} {
-		jumpTable.init(big.NewInt(n))
+		jumpTable := newJumpTable(big.NewInt(n))
 		if !jumpTable[DELEGATECALL].valid {
 			t.Error("Expected DELEGATECALL to be present for block", n)
 		}

+ 7 - 1
core/vm/runtime/env.go

@@ -41,11 +41,13 @@ type Env struct {
 	logs []vm.StructLog
 
 	getHashFn func(uint64) common.Hash
+
+	evm *vm.Vm
 }
 
 // NewEnv returns a new vm.Environment
 func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
-	return &Env{
+	env := &Env{
 		state:      state,
 		origin:     cfg.Origin,
 		coinbase:   cfg.Coinbase,
@@ -54,6 +56,9 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
 		difficulty: cfg.Difficulty,
 		gasLimit:   cfg.GasLimit,
 	}
+	env.evm = vm.EVM(env)
+
+	return env
 }
 
 func (self *Env) StructLogs() []vm.StructLog {
@@ -64,6 +69,7 @@ func (self *Env) AddStructLog(log vm.StructLog) {
 	self.logs = append(self.logs, log)
 }
 
+func (self *Env) Vm() *vm.Vm               { return self.evm }
 func (self *Env) Origin() common.Address   { return self.origin }
 func (self *Env) BlockNumber() *big.Int    { return self.number }
 func (self *Env) Coinbase() common.Address { return self.coinbase }

+ 5 - 8
core/vm/vm.go

@@ -30,15 +30,12 @@ import (
 
 // Vm is an EVM and implements VirtualMachine
 type Vm struct {
-	env Environment
+	env       Environment
+	jumpTable vmJumpTable
 }
 
-// New returns a new Vm
-func New(env Environment) *Vm {
-	// init the jump table. Also prepares the homestead changes
-	jumpTable.init(env.BlockNumber())
-
-	return &Vm{env: env}
+func EVM(env Environment) *Vm {
+	return &Vm{env: env, jumpTable: newJumpTable(env.BlockNumber())}
 }
 
 // Run loops and evaluates the contract's code with the given input data
@@ -169,7 +166,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
 		mem.Resize(newMemSize.Uint64())
 		// Add a log message
 		self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil)
-		if opPtr := jumpTable[op]; opPtr.valid {
+		if opPtr := self.jumpTable[op]; opPtr.valid {
 			if opPtr.fn != nil {
 				opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
 			} else {

+ 1 - 1
core/vm/vm_jit_fake.go

@@ -22,5 +22,5 @@ import "fmt"
 
 func NewJitVm(env Environment) VirtualMachine {
 	fmt.Printf("Warning! EVM JIT not enabled.\n")
-	return New(env)
+	return EVM(env)
 }

+ 5 - 1
core/vm_env.go

@@ -51,10 +51,11 @@ type VMEnv struct {
 	getHashFn func(uint64) common.Hash
 	// structured logging
 	logs []vm.StructLog
+	evm  *vm.Vm
 }
 
 func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.Header) *VMEnv {
-	return &VMEnv{
+	env := &VMEnv{
 		chain:     chain,
 		state:     state,
 		header:    header,
@@ -62,8 +63,11 @@ func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.
 		typ:       vm.StdVmTy,
 		getHashFn: GetHashFn(header.ParentHash, chain),
 	}
+	env.evm = vm.EVM(env)
+	return env
 }
 
+func (self *VMEnv) Vm() *vm.Vm               { return self.evm }
 func (self *VMEnv) Origin() common.Address   { f, _ := self.msg.From(); return f }
 func (self *VMEnv) BlockNumber() *big.Int    { return self.header.Number }
 func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }

+ 7 - 1
tests/util.go

@@ -143,12 +143,15 @@ type Env struct {
 	logs []vm.StructLog
 
 	vmTest bool
+
+	evm *vm.Vm
 }
 
 func NewEnv(state *state.StateDB) *Env {
-	return &Env{
+	env := &Env{
 		state: state,
 	}
+	return env
 }
 
 func (self *Env) StructLogs() []vm.StructLog {
@@ -171,9 +174,12 @@ func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues
 	env.gasLimit = common.Big(envValues["currentGasLimit"])
 	env.Gas = new(big.Int)
 
+	env.evm = vm.EVM(env)
+
 	return env
 }
 
+func (self *Env) Vm() *vm.Vm               { return self.evm }
 func (self *Env) Origin() common.Address   { return self.origin }
 func (self *Env) BlockNumber() *big.Int    { return self.number }
 func (self *Env) Coinbase() common.Address { return self.coinbase }