Skip to content

Commit

Permalink
core/vm: implement RETURNDATA metropolis opcodes
Browse files Browse the repository at this point in the history
  • Loading branch information
obscuren authored and karalabe committed Aug 16, 2017
1 parent 76069ee commit 9bd6068
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 171 deletions.
26 changes: 26 additions & 0 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()

Expand Down
14 changes: 10 additions & 4 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
50 changes: 34 additions & 16 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions core/vm/memory_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
Loading

0 comments on commit 9bd6068

Please sign in to comment.