From 342ae7ce7dfd7a0eab2dd06bfa65199825279f05 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 21 Jan 2016 15:29:58 +0100 Subject: [PATCH] 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. --- cmd/evm/main.go | 9 ++++++++- core/execution.go | 4 ++-- core/vm/common.go | 14 -------------- core/vm/environment.go | 4 +--- core/vm/instructions.go | 1 - core/vm/jit_test.go | 8 ++++++-- core/vm/jump_table.go | 12 +++++------- core/vm/jump_table_test.go | 4 ++-- core/vm/runtime/env.go | 8 +++++++- core/vm/vm.go | 13 +++++-------- core/vm/vm_jit_fake.go | 2 +- core/vm_env.go | 6 +++++- tests/util.go | 8 +++++++- 13 files changed, 49 insertions(+), 44 deletions(-) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index ef679e373f..0ba6820e0c 100644 --- a/cmd/evm/main.go +++ b/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)) } diff --git a/core/execution.go b/core/execution.go index 24c0c93ae9..d90dceafc8 100644 --- a/core/execution.go +++ b/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()) { diff --git a/core/vm/common.go b/core/vm/common.go index 395ed04710..f73bc15270 100644 --- a/core/vm/common.go +++ b/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 { diff --git a/core/vm/environment.go b/core/vm/environment.go index d5d21a45b8..3c530962b8 100644 --- a/core/vm/environment.go +++ b/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) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1e1086b133..c4b4339a20 100644 --- a/core/vm/instructions.go +++ b/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 { diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 19261827b5..5fac0156f0 100644 --- a/core/vm/jit_test.go +++ b/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) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 37d7bb160f..8297d3e1d1 100644 --- a/core/vm/jump_table.go +++ b/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 } diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go index 98d34bef20..2ed1b26fc7 100644 --- a/core/vm/jump_table_test.go +++ b/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) } diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 77519df816..e9bf828ea8 100644 --- a/core/vm/runtime/env.go +++ b/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 } diff --git a/core/vm/vm.go b/core/vm/vm.go index 95d27c64cb..26df8aef47 100644 --- a/core/vm/vm.go +++ b/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 { diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go index 456fcb8d4b..192f3615d7 100644 --- a/core/vm/vm_jit_fake.go +++ b/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) } diff --git a/core/vm_env.go b/core/vm_env.go index 7b9a1a0f9a..0fab4a090d 100644 --- a/core/vm_env.go +++ b/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 } diff --git a/tests/util.go b/tests/util.go index 29f4c9b723..2c749edba9 100644 --- a/tests/util.go +++ b/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 }