core/vm: implement RETURNDATA metropolis opcodes

pull/14981/head
Jeffrey Wilcke 7 years ago committed by Péter Szilágyi
parent 76069eef38
commit 9bd6068fef
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
  1. 26
      core/vm/gas_table.go
  2. 23
      core/vm/instructions.go
  3. 14
      core/vm/interpreter.go
  4. 50
      core/vm/jump_table.go
  5. 4
      core/vm/memory_table.go
  6. 307
      core/vm/opcodes.go

@ -91,6 +91,32 @@ func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *St
return gas, nil
}
func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
var overflow bool
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
words, overflow := bigUint64(stack.Back(2))
if overflow {
return 0, errGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)

@ -31,6 +31,7 @@ import (
var (
bigZero = new(big.Int)
errWriteProtection = errors.New("evm: write protection")
errReadOutOfBound = errors.New("evm: read out of bound")
)
func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
@ -360,6 +361,28 @@ func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
return nil, nil
}
func opReturnDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(evm.interpreter.intPool.get().SetUint64(uint64(len(evm.interpreter.returnData))))
return nil, nil
}
func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
defer evm.interpreter.intPool.put(mOff, cOff, l)
cEnd := new(big.Int).Add(cOff, l)
if cEnd.BitLen() > 64 || uint64(len(evm.interpreter.returnData)) < cEnd.Uint64() {
return nil, errReadOutOfBound
}
memory.Set(mOff.Uint64(), l.Uint64(), evm.interpreter.returnData[cOff.Uint64():cEnd.Uint64()])
return nil, nil
}
func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
a := stack.pop()

@ -60,6 +60,8 @@ type Interpreter struct {
intPool *intPool
readonly bool
// returnData contains the last call's return data
returnData []byte
}
// NewInterpreter returns a new instance of the Interpreter.
@ -113,6 +115,10 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
in.evm.depth++
defer func() { in.evm.depth-- }()
// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in.returnData = nil
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return nil, nil
@ -213,10 +219,10 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
case !operation.jumps:
pc++
}
// if the operation returned a value make sure that is also set
// the last return data.
if res != nil {
mem.lastReturn = ret
// if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation.
if operation.clearsReturndata {
in.returnData = res
}
}
return nil, nil

@ -53,6 +53,8 @@ type operation struct {
valid bool
// reverts determined whether the operation reverts state
reverts bool
// clearsReturndata determines whether the opertions clears the return data
clearsReturndata bool
}
var (
@ -73,6 +75,19 @@ func NewMetropolisInstructionSet() [256]operation {
memorySize: memoryStaticCall,
valid: true,
}
instructionSet[RETURNDATASIZE] = operation{
execute: opReturnDataSize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
}
instructionSet[RETURNDATACOPY] = operation{
execute: opReturnDataCopy,
gasCost: gasReturnDataCopy,
validateStack: makeStackFunc(3, 0),
memorySize: memoryReturnDataCopy,
valid: true,
}
return instructionSet
}
@ -861,26 +876,29 @@ func NewFrontierInstructionSet() [256]operation {
writes: true,
},
CREATE: {
execute: opCreate,
gasCost: gasCreate,
validateStack: makeStackFunc(3, 1),
memorySize: memoryCreate,
valid: true,
writes: true,
execute: opCreate,
gasCost: gasCreate,
validateStack: makeStackFunc(3, 1),
memorySize: memoryCreate,
valid: true,
writes: true,
clearsReturndata: true,
},
CALL: {
execute: opCall,
gasCost: gasCall,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
execute: opCall,
gasCost: gasCall,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
clearsReturndata: true,
},
CALLCODE: {
execute: opCallCode,
gasCost: gasCallCode,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
execute: opCallCode,
gasCost: gasCallCode,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
clearsReturndata: true,
},
RETURN: {
execute: opReturn,

@ -30,6 +30,10 @@ func memoryCalldataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
}
func memoryReturnDataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
}
func memoryCodeCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2))
}

@ -82,10 +82,11 @@ const (
GASPRICE
EXTCODESIZE
EXTCODECOPY
RETURNDATASIZE
RETURNDATACOPY
)
const (
// 0x40 range - block operations
BLOCKHASH OpCode = 0x40 + iota
COINBASE
@ -239,27 +240,29 @@ var opCodeToString = map[OpCode]string{
SHA3: "SHA3",
// 0x30 range - closure state
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "GASPRICE",
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "GASPRICE",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
RETURNDATASIZE: "RETURNDATASIZE",
RETURNDATACOPY: "RETURNDATACOPY",
// 0x40 range - block operations
BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT",
// 0x50 range - 'storage' and execution
POP: "POP",
@ -374,137 +377,139 @@ func (o OpCode) String() string {
}
var stringToOp = map[string]OpCode{
"STOP": STOP,
"ADD": ADD,
"MUL": MUL,
"SUB": SUB,
"DIV": DIV,
"SDIV": SDIV,
"MOD": MOD,
"SMOD": SMOD,
"EXP": EXP,
"NOT": NOT,
"LT": LT,
"GT": GT,
"SLT": SLT,
"SGT": SGT,
"EQ": EQ,
"ISZERO": ISZERO,
"SIGNEXTEND": SIGNEXTEND,
"AND": AND,
"OR": OR,
"XOR": XOR,
"BYTE": BYTE,
"ADDMOD": ADDMOD,
"MULMOD": MULMOD,
"SHA3": SHA3,
"ADDRESS": ADDRESS,
"BALANCE": BALANCE,
"ORIGIN": ORIGIN,
"CALLER": CALLER,
"CALLVALUE": CALLVALUE,
"CALLDATALOAD": CALLDATALOAD,
"CALLDATASIZE": CALLDATASIZE,
"CALLDATACOPY": CALLDATACOPY,
"DELEGATECALL": DELEGATECALL,
"STATICCALL": STATICCALL,
"CODESIZE": CODESIZE,
"CODECOPY": CODECOPY,
"GASPRICE": GASPRICE,
"BLOCKHASH": BLOCKHASH,
"COINBASE": COINBASE,
"TIMESTAMP": TIMESTAMP,
"NUMBER": NUMBER,
"DIFFICULTY": DIFFICULTY,
"GASLIMIT": GASLIMIT,
"EXTCODESIZE": EXTCODESIZE,
"EXTCODECOPY": EXTCODECOPY,
"POP": POP,
"MLOAD": MLOAD,
"MSTORE": MSTORE,
"MSTORE8": MSTORE8,
"SLOAD": SLOAD,
"SSTORE": SSTORE,
"JUMP": JUMP,
"JUMPI": JUMPI,
"PC": PC,
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
"PUSH1": PUSH1,
"PUSH2": PUSH2,
"PUSH3": PUSH3,
"PUSH4": PUSH4,
"PUSH5": PUSH5,
"PUSH6": PUSH6,
"PUSH7": PUSH7,
"PUSH8": PUSH8,
"PUSH9": PUSH9,
"PUSH10": PUSH10,
"PUSH11": PUSH11,
"PUSH12": PUSH12,
"PUSH13": PUSH13,
"PUSH14": PUSH14,
"PUSH15": PUSH15,
"PUSH16": PUSH16,
"PUSH17": PUSH17,
"PUSH18": PUSH18,
"PUSH19": PUSH19,
"PUSH20": PUSH20,
"PUSH21": PUSH21,
"PUSH22": PUSH22,
"PUSH23": PUSH23,
"PUSH24": PUSH24,
"PUSH25": PUSH25,
"PUSH26": PUSH26,
"PUSH27": PUSH27,
"PUSH28": PUSH28,
"PUSH29": PUSH29,
"PUSH30": PUSH30,
"PUSH31": PUSH31,
"PUSH32": PUSH32,
"DUP1": DUP1,
"DUP2": DUP2,
"DUP3": DUP3,
"DUP4": DUP4,
"DUP5": DUP5,
"DUP6": DUP6,
"DUP7": DUP7,
"DUP8": DUP8,
"DUP9": DUP9,
"DUP10": DUP10,
"DUP11": DUP11,
"DUP12": DUP12,
"DUP13": DUP13,
"DUP14": DUP14,
"DUP15": DUP15,
"DUP16": DUP16,
"SWAP1": SWAP1,
"SWAP2": SWAP2,
"SWAP3": SWAP3,
"SWAP4": SWAP4,
"SWAP5": SWAP5,
"SWAP6": SWAP6,
"SWAP7": SWAP7,
"SWAP8": SWAP8,
"SWAP9": SWAP9,
"SWAP10": SWAP10,
"SWAP11": SWAP11,
"SWAP12": SWAP12,
"SWAP13": SWAP13,
"SWAP14": SWAP14,
"SWAP15": SWAP15,
"SWAP16": SWAP16,
"LOG0": LOG0,
"LOG1": LOG1,
"LOG2": LOG2,
"LOG3": LOG3,
"LOG4": LOG4,
"CREATE": CREATE,
"CALL": CALL,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
"SELFDESTRUCT": SELFDESTRUCT,
"STOP": STOP,
"ADD": ADD,
"MUL": MUL,
"SUB": SUB,
"DIV": DIV,
"SDIV": SDIV,
"MOD": MOD,
"SMOD": SMOD,
"EXP": EXP,
"NOT": NOT,
"LT": LT,
"GT": GT,
"SLT": SLT,
"SGT": SGT,
"EQ": EQ,
"ISZERO": ISZERO,
"SIGNEXTEND": SIGNEXTEND,
"AND": AND,
"OR": OR,
"XOR": XOR,
"BYTE": BYTE,
"ADDMOD": ADDMOD,
"MULMOD": MULMOD,
"SHA3": SHA3,
"ADDRESS": ADDRESS,
"BALANCE": BALANCE,
"ORIGIN": ORIGIN,
"CALLER": CALLER,
"CALLVALUE": CALLVALUE,
"CALLDATALOAD": CALLDATALOAD,
"CALLDATASIZE": CALLDATASIZE,
"CALLDATACOPY": CALLDATACOPY,
"DELEGATECALL": DELEGATECALL,
"STATICCALL": STATICCALL,
"CODESIZE": CODESIZE,
"CODECOPY": CODECOPY,
"GASPRICE": GASPRICE,
"EXTCODESIZE": EXTCODESIZE,
"EXTCODECOPY": EXTCODECOPY,
"RETURNDATASIZE": RETURNDATASIZE,
"RETURNDATACOPY": RETURNDATACOPY,
"BLOCKHASH": BLOCKHASH,
"COINBASE": COINBASE,
"TIMESTAMP": TIMESTAMP,
"NUMBER": NUMBER,
"DIFFICULTY": DIFFICULTY,
"GASLIMIT": GASLIMIT,
"POP": POP,
"MLOAD": MLOAD,
"MSTORE": MSTORE,
"MSTORE8": MSTORE8,
"SLOAD": SLOAD,
"SSTORE": SSTORE,
"JUMP": JUMP,
"JUMPI": JUMPI,
"PC": PC,
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
"PUSH1": PUSH1,
"PUSH2": PUSH2,
"PUSH3": PUSH3,
"PUSH4": PUSH4,
"PUSH5": PUSH5,
"PUSH6": PUSH6,
"PUSH7": PUSH7,
"PUSH8": PUSH8,
"PUSH9": PUSH9,
"PUSH10": PUSH10,
"PUSH11": PUSH11,
"PUSH12": PUSH12,
"PUSH13": PUSH13,
"PUSH14": PUSH14,
"PUSH15": PUSH15,
"PUSH16": PUSH16,
"PUSH17": PUSH17,
"PUSH18": PUSH18,
"PUSH19": PUSH19,
"PUSH20": PUSH20,
"PUSH21": PUSH21,
"PUSH22": PUSH22,
"PUSH23": PUSH23,
"PUSH24": PUSH24,
"PUSH25": PUSH25,
"PUSH26": PUSH26,
"PUSH27": PUSH27,
"PUSH28": PUSH28,
"PUSH29": PUSH29,
"PUSH30": PUSH30,
"PUSH31": PUSH31,
"PUSH32": PUSH32,
"DUP1": DUP1,
"DUP2": DUP2,
"DUP3": DUP3,
"DUP4": DUP4,
"DUP5": DUP5,
"DUP6": DUP6,
"DUP7": DUP7,
"DUP8": DUP8,
"DUP9": DUP9,
"DUP10": DUP10,
"DUP11": DUP11,
"DUP12": DUP12,
"DUP13": DUP13,
"DUP14": DUP14,
"DUP15": DUP15,
"DUP16": DUP16,
"SWAP1": SWAP1,
"SWAP2": SWAP2,
"SWAP3": SWAP3,
"SWAP4": SWAP4,
"SWAP5": SWAP5,
"SWAP6": SWAP6,
"SWAP7": SWAP7,
"SWAP8": SWAP8,
"SWAP9": SWAP9,
"SWAP10": SWAP10,
"SWAP11": SWAP11,
"SWAP12": SWAP12,
"SWAP13": SWAP13,
"SWAP14": SWAP14,
"SWAP15": SWAP15,
"SWAP16": SWAP16,
"LOG0": LOG0,
"LOG1": LOG1,
"LOG2": LOG2,
"LOG3": LOG3,
"LOG4": LOG4,
"CREATE": CREATE,
"CALL": CALL,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
"SELFDESTRUCT": SELFDESTRUCT,
}
func StringToOp(str string) OpCode {

Loading…
Cancel
Save