core/vm: implement RETURNDATA metropolis opcodes

This commit is contained in:
Jeffrey Wilcke 2017-08-16 13:07:33 +03:00 committed by Péter Szilágyi
parent 76069eef38
commit 9bd6068fef
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
6 changed files with 253 additions and 171 deletions

View File

@ -91,6 +91,32 @@ func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *St
return gas, nil 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) { func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
y, x = stack.Back(1), stack.Back(0) y, x = stack.Back(1), stack.Back(0)

View File

@ -31,6 +31,7 @@ import (
var ( var (
bigZero = new(big.Int) bigZero = new(big.Int)
errWriteProtection = errors.New("evm: write protection") 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) { 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 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) { func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
a := stack.pop() a := stack.pop()

View File

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

View File

@ -53,6 +53,8 @@ type operation struct {
valid bool valid bool
// reverts determined whether the operation reverts state // reverts determined whether the operation reverts state
reverts bool reverts bool
// clearsReturndata determines whether the opertions clears the return data
clearsReturndata bool
} }
var ( var (
@ -73,6 +75,19 @@ func NewMetropolisInstructionSet() [256]operation {
memorySize: memoryStaticCall, memorySize: memoryStaticCall,
valid: true, 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 return instructionSet
} }
@ -867,6 +882,7 @@ func NewFrontierInstructionSet() [256]operation {
memorySize: memoryCreate, memorySize: memoryCreate,
valid: true, valid: true,
writes: true, writes: true,
clearsReturndata: true,
}, },
CALL: { CALL: {
execute: opCall, execute: opCall,
@ -874,6 +890,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(7, 1), validateStack: makeStackFunc(7, 1),
memorySize: memoryCall, memorySize: memoryCall,
valid: true, valid: true,
clearsReturndata: true,
}, },
CALLCODE: { CALLCODE: {
execute: opCallCode, execute: opCallCode,
@ -881,6 +898,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(7, 1), validateStack: makeStackFunc(7, 1),
memorySize: memoryCall, memorySize: memoryCall,
valid: true, valid: true,
clearsReturndata: true,
}, },
RETURN: { RETURN: {
execute: opReturn, execute: opReturn,

View File

@ -30,6 +30,10 @@ func memoryCalldataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2)) 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 { func memoryCodeCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize(stack.Back(0), stack.Back(2))
} }

View File

@ -82,10 +82,11 @@ const (
GASPRICE GASPRICE
EXTCODESIZE EXTCODESIZE
EXTCODECOPY EXTCODECOPY
RETURNDATASIZE
RETURNDATACOPY
) )
const ( const (
// 0x40 range - block operations // 0x40 range - block operations
BLOCKHASH OpCode = 0x40 + iota BLOCKHASH OpCode = 0x40 + iota
COINBASE COINBASE
@ -250,6 +251,10 @@ var opCodeToString = map[OpCode]string{
CODESIZE: "CODESIZE", CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY", CODECOPY: "CODECOPY",
GASPRICE: "GASPRICE", GASPRICE: "GASPRICE",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
RETURNDATASIZE: "RETURNDATASIZE",
RETURNDATACOPY: "RETURNDATACOPY",
// 0x40 range - block operations // 0x40 range - block operations
BLOCKHASH: "BLOCKHASH", BLOCKHASH: "BLOCKHASH",
@ -258,8 +263,6 @@ var opCodeToString = map[OpCode]string{
NUMBER: "NUMBER", NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY", DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP: "POP", POP: "POP",
@ -411,14 +414,16 @@ var stringToOp = map[string]OpCode{
"CODESIZE": CODESIZE, "CODESIZE": CODESIZE,
"CODECOPY": CODECOPY, "CODECOPY": CODECOPY,
"GASPRICE": GASPRICE, "GASPRICE": GASPRICE,
"EXTCODESIZE": EXTCODESIZE,
"EXTCODECOPY": EXTCODECOPY,
"RETURNDATASIZE": RETURNDATASIZE,
"RETURNDATACOPY": RETURNDATACOPY,
"BLOCKHASH": BLOCKHASH, "BLOCKHASH": BLOCKHASH,
"COINBASE": COINBASE, "COINBASE": COINBASE,
"TIMESTAMP": TIMESTAMP, "TIMESTAMP": TIMESTAMP,
"NUMBER": NUMBER, "NUMBER": NUMBER,
"DIFFICULTY": DIFFICULTY, "DIFFICULTY": DIFFICULTY,
"GASLIMIT": GASLIMIT, "GASLIMIT": GASLIMIT,
"EXTCODESIZE": EXTCODESIZE,
"EXTCODECOPY": EXTCODECOPY,
"POP": POP, "POP": POP,
"MLOAD": MLOAD, "MLOAD": MLOAD,
"MSTORE": MSTORE, "MSTORE": MSTORE,