From 87bbc432ab27aed8f5123a9739bc61e5258ad8fb Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Fri, 10 Mar 2017 20:03:13 +0100 Subject: [PATCH] core/vm: separated fork instruction sets --- core/vm/gas_table.go | 29 +++++++++++++++ core/vm/instructions.go | 25 ------------- core/vm/interpreter.go | 9 ++++- core/vm/jump_table.go | 81 +++++++++++++++++++++++++---------------- core/vm/memory_table.go | 7 ++++ 5 files changed, 94 insertions(+), 57 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 49ab2ed062..f1e32a50e5 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -429,6 +429,35 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St return gas, nil } +func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + panic("verify this one") + + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { + return 0, errGasUintOverflow + } + + cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return GasFastestStep, nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index dd41202687..065db0561a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -574,11 +574,6 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // EIP116 availability check; return an invalid opcode error on fault - if !evm.ChainConfig().IsMetropolis(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", STATIC_CALL) - } - // pop gas gas := stack.pop().Uint64() // pop address @@ -675,12 +670,6 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack } func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // if not homestead return an error. DELEGATECALL is not supported - // during pre-homestead. - if !evm.ChainConfig().IsHomestead(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) - } - gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) @@ -709,12 +698,6 @@ func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } func opRevert(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // if not metropolis return an error. REVERT is not supported - // during pre-metropolis. - if !evm.ChainConfig().IsMetropolis(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", REVERT) - } - offset, size := stack.pop(), stack.pop() ret := memory.GetPtr(offset.Int64(), size.Int64()) @@ -736,19 +719,11 @@ func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * } func opReturnDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - if !evm.ChainConfig().IsMetropolis(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", RETURNDATASIZE) - } - stack.push(evm.interpreter.intPool.get().SetUint64(uint64(len(memory.lastReturn)))) return nil, nil } func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - if !evm.ChainConfig().IsMetropolis(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", RETURNDATACOPY) - } - var ( mOff = stack.pop() cOff = stack.pop() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b003684c08..f571744739 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -71,7 +71,14 @@ func NewInterpreter(env *EVM, cfg Config) *Interpreter { // the jump table was initialised. If it was not // we'll set the default jump table. if !cfg.JumpTable[STOP].valid { - cfg.JumpTable = defaultJumpTable + switch { + case env.ChainConfig().IsMetropolis(env.BlockNumber): + cfg.JumpTable = metropolisInstructionSet + case env.ChainConfig().IsHomestead(env.BlockNumber): + cfg.JumpTable = homesteadInstructionSet + default: + cfg.JumpTable = baseInstructionSet + } } return &Interpreter{ diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 8e580b5c6a..2b3a477ef3 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -55,9 +55,57 @@ type operation struct { reverts bool } -var defaultJumpTable = NewJumpTable() +var ( + baseInstructionSet = NewBaseInstructionSet() + homesteadInstructionSet = NewHomesteadInstructionSet() + metropolisInstructionSet = NewMetropolisInstructionSet() +) -func NewJumpTable() [256]operation { +func NewMetropolisInstructionSet() [256]operation { + instructionSet := NewHomesteadInstructionSet() + instructionSet[STATIC_CALL] = operation{ + execute: opStaticCall, + gasCost: gasStaticCall, + validateStack: makeStackFunc(6, 1), + memorySize: memoryStaticCall, + valid: true, + } + instructionSet[REVERT] = operation{ + execute: opRevert, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 0), + valid: true, + reverts: true, + } + instructionSet[RETURNDATASIZE] = operation{ + execute: opReturnDataSize, + gasCost: constGasFunc(0), // TODO + validateStack: makeStackFunc(0, 1), + valid: true, + } + instructionSet[RETURNDATACOPY] = operation{ + execute: opReturnDataCopy, + gasCost: gasReturnDataCopy, + validateStack: makeStackFunc(3, 0), + memorySize: memoryReturnDataCopy, + valid: true, + } + return instructionSet +} + +func NewHomesteadInstructionSet() [256]operation { + instructionSet := NewBaseInstructionSet() + instructionSet[DELEGATECALL] = operation{ + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: makeStackFunc(6, 1), + memorySize: memoryDelegateCall, + valid: true, + } + return instructionSet +} + +func NewBaseInstructionSet() [256]operation { return [256]operation{ STOP: { execute: opStop, @@ -395,15 +443,6 @@ func NewJumpTable() [256]operation { validateStack: makeStackFunc(0, 1), valid: true, }, - // TODO: - // * Determine cost - REVERT: { - execute: opRevert, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 0), - valid: true, - reverts: true, - }, JUMPDEST: { execute: opJumpdest, gasCost: constGasFunc(params.JumpdestGas), @@ -829,19 +868,6 @@ func NewJumpTable() [256]operation { memorySize: memoryLog, valid: true, }, - RETURNDATASIZE: { - execute: opReturnDataSize, - gasCost: constGasFunc(0), // TODO - validateStack: makeStackFunc(0, 1), - valid: true, - }, - RETURNDATACOPY: { - execute: opReturnDataCopy, - gasCost: gasReturnDataCopy, - validateStack: makeStackFunc(3, 0), - memorySize: memoryReturnDataCopy, - valid: true, - }, CREATE: { execute: opCreate, gasCost: gasCreate, @@ -872,13 +898,6 @@ func NewJumpTable() [256]operation { halts: true, valid: true, }, - DELEGATECALL: { - execute: opDelegateCall, - gasCost: gasDelegateCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryDelegateCall, - valid: true, - }, SELFDESTRUCT: { execute: opSuicide, gasCost: gasSuicide, diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 06d3fcb814..51bf21524d 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -62,6 +62,13 @@ func memoryDelegateCall(stack *Stack) *big.Int { return math.BigMax(x, y) } +func memoryStaticCall(stack *Stack) *big.Int { + x := calcMemSize(stack.Back(4), stack.Back(5)) + y := calcMemSize(stack.Back(2), stack.Back(3)) + + return math.BigMax(x, y) +} + func memoryReturn(stack *Stack) *big.Int { return calcMemSize(stack.Back(0), stack.Back(1)) }