aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2017-08-16 18:07:33 +0800
committerPéter Szilágyi <peterke@gmail.com>2017-08-16 18:43:08 +0800
commit9bd6068fefe5687735bed342f3545f515fa717c8 (patch)
treed68767398eedd0a07c055fc8e880c4b4ab565a70
parent76069eef38082089d2eaf99661a80e1a953bf271 (diff)
downloadgo-tangerine-9bd6068fefe5687735bed342f3545f515fa717c8.tar.gz
go-tangerine-9bd6068fefe5687735bed342f3545f515fa717c8.tar.zst
go-tangerine-9bd6068fefe5687735bed342f3545f515fa717c8.zip
core/vm: implement RETURNDATA metropolis opcodes
-rw-r--r--core/vm/gas_table.go26
-rw-r--r--core/vm/instructions.go23
-rw-r--r--core/vm/interpreter.go14
-rw-r--r--core/vm/jump_table.go50
-rw-r--r--core/vm/memory_table.go4
-rw-r--r--core/vm/opcodes.go307
6 files changed, 253 insertions, 171 deletions
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 8a6c2741d..6cdbc5c39 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.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)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index aaa8d7945..0dd9af096 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -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()
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 661ada691..1c7481bc5 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -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
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index f6e8dae66..7fb11021f 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -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,
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 9d293a2f2..9f6533bbc 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -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))
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 51925e8dd..be87cae18 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -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 {