core/vm: implement RETURNDATA metropolis opcodes
This commit is contained in:
parent
76069eef38
commit
9bd6068fef
@ -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)
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user