core/vm: polish RETURNDATA, add missing returns to CALL*

This commit is contained in:
Péter Szilágyi 2017-08-16 13:36:48 +03:00
parent 9bd6068fef
commit 4e0fea4d30
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
7 changed files with 75 additions and 72 deletions

@ -261,9 +261,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// Make sure the readonly is only set if we aren't in readonly yet // Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for // this makes also sure that the readonly flag isn't removed for
// child calls. // child calls.
if !evm.interpreter.readonly { if !evm.interpreter.readOnly {
evm.interpreter.readonly = true evm.interpreter.readOnly = true
defer func() { evm.interpreter.readonly = false }() defer func() { evm.interpreter.readOnly = false }()
} }
var ( var (

@ -65,7 +65,7 @@ func constGasFunc(gas uint64) gasFunc {
} }
} }
func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err

@ -31,7 +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") errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
) )
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) {
@ -243,6 +243,7 @@ func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac
evm.interpreter.intPool.put(y) evm.interpreter.intPool.put(y)
return nil, nil return nil, nil
} }
func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(x.Or(x, y)) stack.push(x.Or(x, y))
@ -250,6 +251,7 @@ func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack
evm.interpreter.intPool.put(y) evm.interpreter.intPool.put(y)
return nil, nil return nil, nil
} }
func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(x.Xor(x, y)) stack.push(x.Xor(x, y))
@ -269,6 +271,7 @@ func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
evm.interpreter.intPool.put(th) evm.interpreter.intPool.put(th)
return nil, nil return nil, nil
} }
func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop() x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(bigZero) > 0 { if z.Cmp(bigZero) > 0 {
@ -282,6 +285,7 @@ func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
evm.interpreter.intPool.put(y, z) evm.interpreter.intPool.put(y, z)
return nil, nil return nil, nil
} }
func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y, z := stack.pop(), stack.pop(), stack.pop() x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(bigZero) > 0 { if z.Cmp(bigZero) > 0 {
@ -339,25 +343,25 @@ func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack
return nil, nil return nil, nil
} }
func opCalldataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCallDataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(new(big.Int).SetBytes(getDataBig(contract.Input, stack.pop(), big32))) stack.push(new(big.Int).SetBytes(getDataBig(contract.Input, stack.pop(), big32)))
return nil, nil return nil, nil
} }
func opCalldataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCallDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input))))
return nil, nil return nil, nil
} }
func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCallDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var ( var (
mOff = stack.pop() memOffset = stack.pop()
cOff = stack.pop() dataOffset = stack.pop()
l = stack.pop() length = stack.pop()
) )
memory.Set(mOff.Uint64(), l.Uint64(), getDataBig(contract.Input, cOff, l)) memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(contract.Input, dataOffset, length))
evm.interpreter.intPool.put(mOff, cOff, l) evm.interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil return nil, nil
} }
@ -368,17 +372,17 @@ func opReturnDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory,
func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var ( var (
mOff = stack.pop() memOffset = stack.pop()
cOff = stack.pop() dataOffset = stack.pop()
l = stack.pop() length = stack.pop()
) )
defer evm.interpreter.intPool.put(mOff, cOff, l) defer evm.interpreter.intPool.put(memOffset, dataOffset, length)
cEnd := new(big.Int).Add(cOff, l) end := new(big.Int).Add(dataOffset, length)
if cEnd.BitLen() > 64 || uint64(len(evm.interpreter.returnData)) < cEnd.Uint64() { if end.BitLen() > 64 || uint64(len(evm.interpreter.returnData)) < end.Uint64() {
return nil, errReadOutOfBound return nil, errReturnDataOutOfBounds
} }
memory.Set(mOff.Uint64(), l.Uint64(), evm.interpreter.returnData[cOff.Uint64():cEnd.Uint64()]) memory.Set(memOffset.Uint64(), length.Uint64(), evm.interpreter.returnData[dataOffset.Uint64():end.Uint64()])
return nil, nil return nil, nil
} }
@ -401,31 +405,28 @@ func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack
func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var ( var (
mOff = stack.pop() memOffset = stack.pop()
cOff = stack.pop() codeOffset = stack.pop()
l = stack.pop() length = stack.pop()
) )
codeCopy := getDataBig(contract.Code, cOff, l) codeCopy := getDataBig(contract.Code, codeOffset, length)
memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) evm.interpreter.intPool.put(memOffset, codeOffset, length)
evm.interpreter.intPool.put(mOff, cOff, l)
return nil, nil return nil, nil
} }
func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var ( var (
addr = common.BigToAddress(stack.pop()) addr = common.BigToAddress(stack.pop())
mOff = stack.pop() memOffset = stack.pop()
cOff = stack.pop() codeOffset = stack.pop()
l = stack.pop() length = stack.pop()
) )
codeCopy := getDataBig(evm.StateDB.GetCode(addr), cOff, l) codeCopy := getDataBig(evm.StateDB.GetCode(addr), codeOffset, length)
memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
evm.interpreter.intPool.put(mOff, cOff, l)
evm.interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil return nil, nil
} }
@ -530,6 +531,7 @@ func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
evm.interpreter.intPool.put(pos) evm.interpreter.intPool.put(pos)
return nil, nil return nil, nil
} }
func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
pos, cond := stack.pop(), stack.pop() pos, cond := stack.pop(), stack.pop()
if cond.Sign() != 0 { if cond.Sign() != 0 {
@ -545,6 +547,7 @@ func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St
evm.interpreter.intPool.put(pos, cond) evm.interpreter.intPool.put(pos, cond)
return nil, nil return nil, nil
} }
func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
return nil, nil return nil, nil
} }

@ -59,9 +59,8 @@ type Interpreter struct {
gasTable params.GasTable gasTable params.GasTable
intPool *intPool intPool *intPool
readonly bool readOnly bool // Whether to throw on stateful modifications
// returnData contains the last call's return data returnData []byte // Last CALL's return data for subsequent reuse
returnData []byte
} }
// NewInterpreter returns a new instance of the Interpreter. // NewInterpreter returns a new instance of the Interpreter.
@ -90,7 +89,7 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsMetropolis { if in.evm.chainRules.IsMetropolis {
if in.readonly { if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no // If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item // state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transfering value from one // for a call operation is the value. Transfering value from one
@ -221,7 +220,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
} }
// if the operation clears the return data (e.g. it has returning data) // if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation. // set the last return to the result of the operation.
if operation.clearsReturndata { if operation.returns {
in.returnData = res in.returnData = res
} }
} }

@ -53,8 +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 // returns determines whether the opertions sets the return data
clearsReturndata bool returns bool
} }
var ( var (
@ -74,6 +74,7 @@ func NewMetropolisInstructionSet() [256]operation {
validateStack: makeStackFunc(6, 1), validateStack: makeStackFunc(6, 1),
memorySize: memoryStaticCall, memorySize: memoryStaticCall,
valid: true, valid: true,
returns: true,
} }
instructionSet[RETURNDATASIZE] = operation{ instructionSet[RETURNDATASIZE] = operation{
execute: opReturnDataSize, execute: opReturnDataSize,
@ -101,6 +102,7 @@ func NewHomesteadInstructionSet() [256]operation {
validateStack: makeStackFunc(6, 1), validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall, memorySize: memoryDelegateCall,
valid: true, valid: true,
returns: true,
} }
return instructionSet return instructionSet
} }
@ -286,22 +288,22 @@ func NewFrontierInstructionSet() [256]operation {
valid: true, valid: true,
}, },
CALLDATALOAD: { CALLDATALOAD: {
execute: opCalldataLoad, execute: opCallDataLoad,
gasCost: constGasFunc(GasFastestStep), gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(1, 1), validateStack: makeStackFunc(1, 1),
valid: true, valid: true,
}, },
CALLDATASIZE: { CALLDATASIZE: {
execute: opCalldataSize, execute: opCallDataSize,
gasCost: constGasFunc(GasQuickStep), gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1), validateStack: makeStackFunc(0, 1),
valid: true, valid: true,
}, },
CALLDATACOPY: { CALLDATACOPY: {
execute: opCalldataCopy, execute: opCallDataCopy,
gasCost: gasCalldataCopy, gasCost: gasCallDataCopy,
validateStack: makeStackFunc(3, 0), validateStack: makeStackFunc(3, 0),
memorySize: memoryCalldataCopy, memorySize: memoryCallDataCopy,
valid: true, valid: true,
}, },
CODESIZE: { CODESIZE: {
@ -882,7 +884,7 @@ func NewFrontierInstructionSet() [256]operation {
memorySize: memoryCreate, memorySize: memoryCreate,
valid: true, valid: true,
writes: true, writes: true,
clearsReturndata: true, returns: true,
}, },
CALL: { CALL: {
execute: opCall, execute: opCall,
@ -890,7 +892,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(7, 1), validateStack: makeStackFunc(7, 1),
memorySize: memoryCall, memorySize: memoryCall,
valid: true, valid: true,
clearsReturndata: true, returns: true,
}, },
CALLCODE: { CALLCODE: {
execute: opCallCode, execute: opCallCode,
@ -898,7 +900,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(7, 1), validateStack: makeStackFunc(7, 1),
memorySize: memoryCall, memorySize: memoryCall,
valid: true, valid: true,
clearsReturndata: true, returns: true,
}, },
RETURN: { RETURN: {
execute: opReturn, execute: opReturn,

@ -22,7 +22,6 @@ import "fmt"
type Memory struct { type Memory struct {
store []byte store []byte
lastGasCost uint64 lastGasCost uint64
lastReturn []byte
} }
func NewMemory() *Memory { func NewMemory() *Memory {

@ -26,7 +26,7 @@ func memorySha3(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1)) return calcMemSize(stack.Back(0), stack.Back(1))
} }
func memoryCalldataCopy(stack *Stack) *big.Int { func memoryCallDataCopy(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize(stack.Back(0), stack.Back(2))
} }