From 61404979ed9b4f88b0fe3fc02beb2ef47149cec6 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Fri, 27 Nov 2015 15:40:29 +0100 Subject: [release/1.3.4] parmas, crypto, core, core/vm: homestead consensus protocol changes * change gas cost for contract creating txs * invalidate signature with s value greater than secp256k1 N / 2 * OOG contract creation if not enough gas to store code * new difficulty adjustment algorithm * new DELEGATECALL op code Conflicts: core/vm/environment.go crypto/crypto.go crypto/secp256k1/secp256.go eth/api.go --- core/vm/contract.go | 16 +++++++++++++++- core/vm/contracts.go | 3 ++- core/vm/environment.go | 5 +++++ core/vm/errors.go | 1 + core/vm/gas.go | 1 + core/vm/instructions.go | 49 ++++++++++++++++++++++--------------------------- core/vm/jump_table.go | 3 ++- core/vm/opcodes.go | 12 +++++++----- core/vm/runtime/env.go | 4 ++++ core/vm/vm.go | 38 ++++++++++++++++++++++++++++++++++++-- 10 files changed, 95 insertions(+), 37 deletions(-) (limited to 'core/vm') diff --git a/core/vm/contract.go b/core/vm/contract.go index 95417e747..e44647698 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -26,11 +26,13 @@ import ( type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() common.Address + SetAddress(common.Address) + Value() *big.Int SetCode([]byte) } // Contract represents an ethereum contract in the state database. It contains -// the the contract code, calling arguments. Contract implements ContractReg +// the the contract code, calling arguments. Contract implements ContractRef type Contract struct { caller ContractRef self ContractRef @@ -44,6 +46,8 @@ type Contract struct { value, Gas, UsedGas, Price *big.Int Args []byte + + DelegateCall bool } // Create a new context for the given data items. @@ -113,6 +117,16 @@ func (c *Contract) Address() common.Address { return c.self.Address() } +// SetAddress sets the contracts address +func (c *Contract) SetAddress(addr common.Address) { + c.self.SetAddress(addr) +} + +// Value returns the contracts value (sent to it from it's caller) +func (c *Contract) Value() *big.Int { + return c.value +} + // SetCode sets the code to the contract func (self *Contract) SetCode(code []byte) { self.Code = code diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 22cb9eab2..f204432a2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -93,7 +93,8 @@ func ecrecoverFunc(in []byte) []byte { vbig := common.Bytes2Big(in[32:64]) v := byte(vbig.Uint64()) - if !crypto.ValidateSignatureValues(v, r, s) { + // tighter sig s values in homestead only apply to tx sigs + if !crypto.ValidateSignatureValues(v, r, s, false) { glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid") return nil } diff --git a/core/vm/environment.go b/core/vm/environment.go index 299d12674..dc60af2ca 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -70,6 +70,8 @@ type Environment interface { Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) // Take another's contract code and execute within our own context CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) + // Same as CallCode except sender and value is propagated from parent to child scope + DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) // Create a new contract Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) } @@ -119,6 +121,9 @@ type Account interface { SetNonce(uint64) Balance() *big.Int Address() common.Address + SetAddress(common.Address) ReturnGas(*big.Int, *big.Int) SetCode([]byte) + EachStorage(cb func(key, value []byte)) + Value() *big.Int } diff --git a/core/vm/errors.go b/core/vm/errors.go index e2fc84065..116fbe456 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -24,4 +24,5 @@ import ( ) var OutOfGasError = errors.New("Out of gas") +var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas") var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth) diff --git a/core/vm/gas.go b/core/vm/gas.go index bff0ac91b..09feddd7d 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -136,6 +136,7 @@ var _baseCheck = map[OpCode]req{ CREATE: {3, params.CreateGas, 1}, CALL: {7, params.CallGas, 1}, CALLCODE: {7, params.CallGas, 1}, + DELEGATECALL: {6, params.CallGas, 1}, JUMPDEST: {0, params.JumpdestGas, 0}, SUICIDE: {1, Zero, 0}, RETURN: {2, Zero, 0}, diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 2e868521e..9d3d4e6fe 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -337,7 +337,13 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract } func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { - stack.push(common.Bytes2Big(contract.caller.Address().Bytes())) + var bigAddr *big.Int + if contract.DelegateCall { + bigAddr = env.Origin().Big() + } else { + bigAddr = contract.caller.Address().Big() + } + stack.push(bigAddr) } func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { @@ -485,7 +491,6 @@ func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { loc := common.BigToHash(stack.pop()) val := stack.pop() - env.Db().SetState(contract.Address(), loc, common.BigToHash(val)) } @@ -509,31 +514,6 @@ func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, m } func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { - var ( - value = stack.pop() - offset, size = stack.pop(), stack.pop() - input = memory.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(contract.Gas) - addr common.Address - ret []byte - suberr error - ) - - contract.UseGas(contract.Gas) - ret, addr, suberr = env.Create(contract, input, gas, contract.Price, value) - if suberr != nil { - stack.push(new(big.Int)) - } else { - // gas < len(ret) * Createinstr.dataGas == NO_CODE - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if contract.UseGas(dataGas) { - env.Db().SetCode(addr, ret) - } - - stack.push(addr.Big()) - - } } func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { @@ -598,6 +578,21 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra } } +func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + + 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 { + stack.push(big.NewInt(1)) + memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) + } +} + func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { } func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ab899647f..222c93854 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -62,9 +62,10 @@ func init() { jumpTable[PC] = jumpPtr{nil, true} jumpTable[MSIZE] = jumpPtr{opMsize, true} jumpTable[GAS] = jumpPtr{opGas, true} - jumpTable[CREATE] = jumpPtr{opCreate, true} + jumpTable[CREATE] = jumpPtr{nil, true} jumpTable[CALL] = jumpPtr{opCall, true} jumpTable[CALLCODE] = jumpPtr{opCallCode, true} + jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true} jumpTable[LOG0] = jumpPtr{makeLog(0), true} jumpTable[LOG1] = jumpPtr{makeLog(1), true} jumpTable[LOG2] = jumpPtr{makeLog(2), true} diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index dc4139092..00593ae95 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -200,6 +200,7 @@ const ( CALL CALLCODE RETURN + DELEGATECALL SUICIDE = 0xff ) @@ -349,11 +350,12 @@ var opCodeToString = map[OpCode]string{ LOG4: "LOG4", // 0xf0 range - CREATE: "CREATE", - CALL: "CALL", - RETURN: "RETURN", - CALLCODE: "CALLCODE", - SUICIDE: "SUICIDE", + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + CALLCODE: "CALLCODE", + DELEGATECALL: "DELEGATECALL", + SUICIDE: "SUICIDE", PUSH: "PUSH", DUP: "DUP", diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 22f9ea14d..77519df81 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -101,6 +101,10 @@ func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byt return core.CallCode(self, caller, addr, data, gas, price, value) } +func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { + return core.DelegateCall(self, me, addr, data, gas, price) +} + func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { return core.Create(self, caller, data, gas, price, value) } diff --git a/core/vm/vm.go b/core/vm/vm.go index 4b03e55f0..47879db62 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -160,7 +160,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { // Get the memory location of pc op = contract.GetOp(pc) - // calculate the new memory size and gas price for the current executing opcode newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack) if err != nil { @@ -177,7 +176,6 @@ 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.fn != nil { opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack) @@ -201,6 +199,35 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { continue } + case CREATE: + var ( + value = stack.pop() + offset, size = stack.pop(), stack.pop() + input = mem.Get(offset.Int64(), size.Int64()) + gas = new(big.Int).Set(contract.Gas) + addr common.Address + ret []byte + suberr error + ) + contract.UseGas(contract.Gas) + ret, addr, suberr = self.env.Create(contract, input, gas, contract.Price, value) + if suberr != nil { + stack.push(new(big.Int)) + } else { + // gas < len(ret) * Createinstr.dataGas == NO_CODE + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, params.CreateDataGas) + if contract.UseGas(dataGas) { + self.env.Db().SetCode(addr, ret) + } else { + if params.IsHomestead(self.env.BlockNumber()) { + stack.push(new(big.Int)) + return nil, CodeStoreOutOfGasError + } + } + stack.push(addr.Big()) + } + case RETURN: offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(offset.Int64(), size.Int64()) @@ -345,6 +372,13 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef 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 = common.BigMax(x, y) + case DELEGATECALL: + gas.Add(gas, stack.data[stack.len()-1]) + + x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) + y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) + newMemSize = common.BigMax(x, y) } quadMemGas(mem, newMemSize, gas) -- cgit v1.2.3 From 587bafaa9faba2d5b0b19c1a0e3e709218e25912 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Tue, 19 Jan 2016 23:50:00 +0100 Subject: [release/1.3.4] core, core/vm, crypto: fixes for homestead * Removed some strange code that didn't apply state reverting properly * Refactored code setting from vm & state transition to the executioner * Updated tests Conflicts: common/registrar/ethreg/api.go core/tx_pool.go core/vm/jit_test.go --- core/vm/contract.go | 44 +++++++++++++++++++++++++-------------- core/vm/environment.go | 1 - core/vm/instructions.go | 27 +++++++++++++++++------- core/vm/jit.go | 21 ++++++++++++++++--- core/vm/jit_test.go | 22 ++++++++++++-------- core/vm/jump_table.go | 23 +++++++++++++++++---- core/vm/jump_table_test.go | 24 ++++++++++++++++++++++ core/vm/opcodes.go | 1 + core/vm/vm.go | 51 +++++++--------------------------------------- 9 files changed, 132 insertions(+), 82 deletions(-) create mode 100644 core/vm/jump_table_test.go (limited to 'core/vm') diff --git a/core/vm/contract.go b/core/vm/contract.go index e44647698..7c4149c97 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -26,7 +26,6 @@ import ( type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() common.Address - SetAddress(common.Address) Value() *big.Int SetCode([]byte) } @@ -34,8 +33,12 @@ type ContractRef interface { // Contract represents an ethereum contract in the state database. It contains // the the contract code, calling arguments. Contract implements ContractRef type Contract struct { - caller ContractRef - self ContractRef + // CallerAddress is the result of the caller which initialised this + // contract. However when the "call method" is delegated this value + // needs to be initialised to that of the caller's caller. + CallerAddress common.Address + caller ContractRef + self ContractRef jumpdests destinations // result of JUMPDEST analysis. @@ -50,9 +53,9 @@ type Contract struct { DelegateCall bool } -// Create a new context for the given data items. +// NewContract returns a new contract environment for the execution of EVM. func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract { - c := &Contract{caller: caller, self: object, Args: nil} + c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} if parent, ok := caller.(*Contract); ok { // Reuse JUMPDEST analysis from parent context if available. @@ -73,6 +76,16 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big. return c } +// AsDelegate sets the contract to be a delegate call and returns the current +// contract (for chaining calls) +func (c *Contract) AsDelegate() *Contract { + c.DelegateCall = true + // NOTE: caller must, at all times be a contract. It should never happen + // that caller is something other than a Contract. + c.CallerAddress = c.caller.(*Contract).CallerAddress + return c +} + // GetOp returns the n'th element in the contract's byte array func (c *Contract) GetOp(n uint64) OpCode { return OpCode(c.GetByte(n)) @@ -87,13 +100,19 @@ func (c *Contract) GetByte(n uint64) byte { return 0 } -// Return returns the given ret argument and returns any remaining gas to the -// caller -func (c *Contract) Return(ret []byte) []byte { +// Caller returns the caller of the contract. +// +// Caller will recursively call caller when the contract is a delegate +// call, including that of caller's caller. +func (c *Contract) Caller() common.Address { + return c.CallerAddress +} + +// Finalise finalises the contract and returning any remaining gas to the original +// caller. +func (c *Contract) Finalise() { // Return the remaining gas to the caller c.caller.ReturnGas(c.Gas, c.Price) - - return ret } // UseGas attempts the use gas and subtracts it and returns true on success @@ -117,11 +136,6 @@ func (c *Contract) Address() common.Address { return c.self.Address() } -// SetAddress sets the contracts address -func (c *Contract) SetAddress(addr common.Address) { - c.self.SetAddress(addr) -} - // Value returns the contracts value (sent to it from it's caller) func (c *Contract) Value() *big.Int { return c.value diff --git a/core/vm/environment.go b/core/vm/environment.go index dc60af2ca..a58e3ba2b 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -121,7 +121,6 @@ type Account interface { SetNonce(uint64) Balance() *big.Int Address() common.Address - SetAddress(common.Address) ReturnGas(*big.Int, *big.Int) SetCode([]byte) EachStorage(cb func(key, value []byte)) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 9d3d4e6fe..26f7671ff 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -337,13 +337,7 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract } func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { - var bigAddr *big.Int - if contract.DelegateCall { - bigAddr = env.Origin().Big() - } else { - bigAddr = contract.caller.Address().Big() - } - stack.push(bigAddr) + stack.push(contract.Caller().Big()) } func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { @@ -514,6 +508,25 @@ func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, m } func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { + var ( + value = stack.pop() + offset, size = stack.pop(), stack.pop() + input = memory.Get(offset.Int64(), size.Int64()) + gas = new(big.Int).Set(contract.Gas) + ) + contract.UseGas(contract.Gas) + _, addr, suberr := env.Create(contract, input, gas, contract.Price, value) + // Push item on the stack based on the returned error. If the ruleset is + // homestead we must check for CodeStoreOutOfGasError (homestead only + // rule) and treat as an error, if the ruleset is frontier we must + // ignore this error and pretend the operation was successful. + if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + stack.push(new(big.Int)) + } else if suberr != nil && suberr != CodeStoreOutOfGasError { + stack.push(new(big.Int)) + } else { + stack.push(addr.Big()) + } } func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { diff --git a/core/vm/jit.go b/core/vm/jit.go index 1aa7d7ef2..504aab523 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -275,6 +275,11 @@ func CompileProgram(program *Program) (err error) { program.addInstr(op, pc, opGas, nil) case CREATE: program.addInstr(op, pc, opCreate, nil) + case DELEGATECALL: + // Instruction added regardless of homestead phase. + // Homestead (and execution of the opcode) is checked during + // runtime. + program.addInstr(op, pc, opDelegateCall, nil) case CALL: program.addInstr(op, pc, opCall, nil) case CALLCODE: @@ -317,10 +322,14 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env }() } + homestead := params.IsHomestead(env.BlockNumber()) for pc < uint64(len(program.instructions)) { instrCount++ instr := program.instructions[pc] + if instr.Op() == DELEGATECALL && !homestead { + return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op()) + } ret, err := instr.do(program, &pc, env, contract, mem, stack) if err != nil { @@ -328,13 +337,13 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env } if instr.halts() { - return contract.Return(ret), nil + return ret, nil } } contract.Input = nil - return contract.Return(nil), nil + return nil, nil } // validDest checks if the given distination is a valid one given the @@ -457,7 +466,6 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } @@ -470,6 +478,13 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi 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 = common.BigMax(x, y) + case DELEGATECALL: + gas.Add(gas, stack.data[stack.len()-1]) + + x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) + y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) + newMemSize = common.BigMax(x, y) } quadMemGas(mem, newMemSize, gas) diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index aa97e5184..e8e078a46 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -125,14 +125,17 @@ type vmBench struct { type account struct{} -func (account) SubBalance(amount *big.Int) {} -func (account) AddBalance(amount *big.Int) {} -func (account) SetBalance(*big.Int) {} -func (account) SetNonce(uint64) {} -func (account) Balance() *big.Int { return nil } -func (account) Address() common.Address { return common.Address{} } -func (account) ReturnGas(*big.Int, *big.Int) {} -func (account) SetCode([]byte) {} +func (account) SubBalance(amount *big.Int) {} +func (account) AddBalance(amount *big.Int) {} +func (account) SetAddress(common.Address) {} +func (account) Value() *big.Int { return nil } +func (account) SetBalance(*big.Int) {} +func (account) SetNonce(uint64) {} +func (account) Balance() *big.Int { return nil } +func (account) Address() common.Address { return common.Address{} } +func (account) ReturnGas(*big.Int, *big.Int) {} +func (account) SetCode([]byte) {} +func (account) EachStorage(cb func(key, value []byte)) {} func runVmBench(test vmBench, b *testing.B) { var sender account @@ -205,3 +208,6 @@ func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { return nil, common.Address{}, nil } +func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { + return nil, nil +} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 222c93854..37d7bb160 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1,13 +1,29 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type jumpPtr struct { fn instrFn valid bool } -var jumpTable [256]jumpPtr +type vmJumpTable [256]jumpPtr + +func (jt vmJumpTable) init(blockNumber *big.Int) { + // 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} @@ -62,10 +78,9 @@ func init() { jumpTable[PC] = jumpPtr{nil, true} jumpTable[MSIZE] = jumpPtr{opMsize, true} jumpTable[GAS] = jumpPtr{opGas, true} - jumpTable[CREATE] = jumpPtr{nil, true} + jumpTable[CREATE] = jumpPtr{opCreate, true} jumpTable[CALL] = jumpPtr{opCall, true} jumpTable[CALLCODE] = jumpPtr{opCallCode, true} - jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true} jumpTable[LOG0] = jumpPtr{makeLog(0), true} jumpTable[LOG1] = jumpPtr{makeLog(1), true} jumpTable[LOG2] = jumpPtr{makeLog(2), true} diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go new file mode 100644 index 000000000..98d34bef2 --- /dev/null +++ b/core/vm/jump_table_test.go @@ -0,0 +1,24 @@ +package vm + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/params" +) + +func TestInit(t *testing.T) { + params.HomesteadBlock = big.NewInt(1) + + jumpTable.init(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)) + if !jumpTable[DELEGATECALL].valid { + t.Error("Expected DELEGATECALL to be present for block", n) + } + } +} diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 00593ae95..7d861f1de 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -404,6 +404,7 @@ var stringToOp = map[string]OpCode{ "CALLDATALOAD": CALLDATALOAD, "CALLDATASIZE": CALLDATASIZE, "CALLDATACOPY": CALLDATACOPY, + "DELEGATECALL": DELEGATECALL, "CODESIZE": CODESIZE, "CODECOPY": CODECOPY, "GASPRICE": GASPRICE, diff --git a/core/vm/vm.go b/core/vm/vm.go index 47879db62..4b502e3d8 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -35,6 +35,9 @@ type Vm struct { // 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} } @@ -43,16 +46,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { self.env.SetDepth(self.env.Depth() + 1) defer self.env.SetDepth(self.env.Depth() - 1) - // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. - defer func() { - if err != nil { - // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). - contract.UseGas(contract.Gas) - - ret = contract.Return(nil) - } - }() - if contract.CodeAddr != nil { if p := Precompiled[contract.CodeAddr.Str()]; p != nil { return self.RunPrecompiled(p, input, contract) @@ -61,7 +54,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { - return contract.Return(nil), nil + return nil, nil } var ( @@ -199,46 +192,17 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { continue } - case CREATE: - var ( - value = stack.pop() - offset, size = stack.pop(), stack.pop() - input = mem.Get(offset.Int64(), size.Int64()) - gas = new(big.Int).Set(contract.Gas) - addr common.Address - ret []byte - suberr error - ) - contract.UseGas(contract.Gas) - ret, addr, suberr = self.env.Create(contract, input, gas, contract.Price, value) - if suberr != nil { - stack.push(new(big.Int)) - } else { - // gas < len(ret) * Createinstr.dataGas == NO_CODE - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if contract.UseGas(dataGas) { - self.env.Db().SetCode(addr, ret) - } else { - if params.IsHomestead(self.env.BlockNumber()) { - stack.push(new(big.Int)) - return nil, CodeStoreOutOfGasError - } - } - stack.push(addr.Big()) - } - case RETURN: offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(offset.Int64(), size.Int64()) - return contract.Return(ret), nil + return ret, nil case SUICIDE: opSuicide(instruction{}, nil, self.env, contract, mem, stack) fallthrough case STOP: // Stop the contract - return contract.Return(nil), nil + return nil, nil } } } else { @@ -359,7 +323,6 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } @@ -392,7 +355,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co if contract.UseGas(gas) { ret = p.Call(input) - return contract.Return(ret), nil + return ret, nil } else { return nil, OutOfGasError } -- cgit v1.2.3