core/vm: implement metropolis static call opcode
This commit is contained in:
parent
9facf6423d
commit
3d123bcde6
@ -245,6 +245,51 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||||||
return ret, contract.Gas, err
|
return ret, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
|
return nil, gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
|
// limit.
|
||||||
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
return nil, gas, ErrDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// child calls.
|
||||||
|
if !evm.interpreter.readonly {
|
||||||
|
evm.interpreter.readonly = true
|
||||||
|
defer func() { evm.interpreter.readonly = false }()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
to = AccountRef(addr)
|
||||||
|
snapshot = evm.StateDB.Snapshot()
|
||||||
|
)
|
||||||
|
if !evm.StateDB.Exist(addr) {
|
||||||
|
return nil, gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialise a new contract and set the code that is to be used by the
|
||||||
|
// EVM. The contract is a scoped evmironment for this execution context
|
||||||
|
// only.
|
||||||
|
contract := NewContract(caller, to, new(big.Int), gas)
|
||||||
|
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||||
|
|
||||||
|
ret, err = evm.interpreter.Run(snapshot, contract, input)
|
||||||
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
|
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||||
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
|
if err != nil {
|
||||||
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
}
|
||||||
|
return ret, contract.Gas, err
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates a new contract using code as deployment code.
|
// Create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||||
|
@ -423,6 +423,33 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
|
|||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gasStaticCall(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, gt.Calls); overflow {
|
||||||
|
return 0, errGasUintOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Replace the stack item with the new gas calculation. This means that
|
||||||
|
// either the original item is left on the stack or the item is replaced by:
|
||||||
|
// (availableGas - gas) * 63 / 64
|
||||||
|
// We replace the stack item so that it's available when the opCall instruction is
|
||||||
|
// called.
|
||||||
|
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||||
|
|
||||||
|
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||||
|
return 0, errGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
return GasFastestStep, nil
|
return GasFastestStep, nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
@ -28,7 +29,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bigZero = new(big.Int)
|
bigZero = new(big.Int)
|
||||||
|
errWriteProtection = errors.New("evm: write protection")
|
||||||
)
|
)
|
||||||
|
|
||||||
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) {
|
||||||
@ -656,6 +658,35 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
// pop gas
|
||||||
|
gas := stack.pop().Uint64()
|
||||||
|
// pop address
|
||||||
|
addr := stack.pop()
|
||||||
|
// pop input size and offset
|
||||||
|
inOffset, inSize := stack.pop(), stack.pop()
|
||||||
|
// pop return size and offset
|
||||||
|
retOffset, retSize := stack.pop(), stack.pop()
|
||||||
|
|
||||||
|
address := common.BigToAddress(addr)
|
||||||
|
|
||||||
|
// Get the arguments from the memory
|
||||||
|
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||||
|
|
||||||
|
ret, returnGas, err := evm.StaticCall(contract, address, args, gas)
|
||||||
|
if err != nil {
|
||||||
|
stack.push(new(big.Int))
|
||||||
|
} else {
|
||||||
|
stack.push(big.NewInt(1))
|
||||||
|
|
||||||
|
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
|
}
|
||||||
|
contract.Gas += returnGas
|
||||||
|
|
||||||
|
evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
offset, size := stack.pop(), stack.pop()
|
offset, size := stack.pop(), stack.pop()
|
||||||
ret := memory.GetPtr(offset.Int64(), size.Int64())
|
ret := memory.GetPtr(offset.Int64(), size.Int64())
|
||||||
|
@ -69,6 +69,8 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
|
|||||||
// we'll set the default jump table.
|
// we'll set the default jump table.
|
||||||
if !cfg.JumpTable[STOP].valid {
|
if !cfg.JumpTable[STOP].valid {
|
||||||
switch {
|
switch {
|
||||||
|
case evm.ChainConfig().IsMetropolis(evm.BlockNumber):
|
||||||
|
cfg.JumpTable = metropolisInstructionSet
|
||||||
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
|
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
|
||||||
cfg.JumpTable = homesteadInstructionSet
|
cfg.JumpTable = homesteadInstructionSet
|
||||||
default:
|
default:
|
||||||
@ -85,6 +87,19 @@ 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.readonly {
|
||||||
|
// if the interpreter is operating in readonly mode, make sure no
|
||||||
|
// state-modifying operation is performed. The 4th stack item
|
||||||
|
// for a call operation is the value. Transfering value from one
|
||||||
|
// account to the others means the state is modified and should also
|
||||||
|
// return with an error.
|
||||||
|
if operation.writes ||
|
||||||
|
((op == CALL || op == CALLCODE) && stack.Back(3).BitLen() > 0) {
|
||||||
|
return errWriteProtection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +110,7 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
|
|||||||
// considered a revert-and-consume-all-gas operation. No error specific checks
|
// considered a revert-and-consume-all-gas operation. No error specific checks
|
||||||
// should be handled to reduce complexity and errors further down the in.
|
// should be handled to reduce complexity and errors further down the in.
|
||||||
func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
|
func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
|
||||||
|
// Increment the call depth which is restricted to 1024
|
||||||
in.evm.depth++
|
in.evm.depth++
|
||||||
defer func() { in.evm.depth-- }()
|
defer func() { in.evm.depth-- }()
|
||||||
|
|
||||||
|
@ -56,10 +56,26 @@ type operation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
frontierInstructionSet = NewFrontierInstructionSet()
|
frontierInstructionSet = NewFrontierInstructionSet()
|
||||||
homesteadInstructionSet = NewHomesteadInstructionSet()
|
homesteadInstructionSet = NewHomesteadInstructionSet()
|
||||||
|
metropolisInstructionSet = NewMetropolisInstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewMetropolisInstructionSet returns the frontier, homestead and
|
||||||
|
// metropolis instructions.
|
||||||
|
func NewMetropolisInstructionSet() [256]operation {
|
||||||
|
// instructions that can be executed during the homestead phase.
|
||||||
|
instructionSet := NewHomesteadInstructionSet()
|
||||||
|
instructionSet[STATICCALL] = operation{
|
||||||
|
execute: opStaticCall,
|
||||||
|
gasCost: gasStaticCall,
|
||||||
|
validateStack: makeStackFunc(6, 1),
|
||||||
|
memorySize: memoryStaticCall,
|
||||||
|
valid: true,
|
||||||
|
}
|
||||||
|
return instructionSet
|
||||||
|
}
|
||||||
|
|
||||||
// NewHomesteadInstructionSet returns the frontier and homestead
|
// NewHomesteadInstructionSet returns the frontier and homestead
|
||||||
// instructions that can be executed during the homestead phase.
|
// instructions that can be executed during the homestead phase.
|
||||||
func NewHomesteadInstructionSet() [256]operation {
|
func NewHomesteadInstructionSet() [256]operation {
|
||||||
@ -810,6 +826,7 @@ func NewFrontierInstructionSet() [256]operation {
|
|||||||
validateStack: makeStackFunc(2, 0),
|
validateStack: makeStackFunc(2, 0),
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
writes: true,
|
||||||
},
|
},
|
||||||
LOG1: {
|
LOG1: {
|
||||||
execute: makeLog(1),
|
execute: makeLog(1),
|
||||||
@ -817,6 +834,7 @@ func NewFrontierInstructionSet() [256]operation {
|
|||||||
validateStack: makeStackFunc(3, 0),
|
validateStack: makeStackFunc(3, 0),
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
writes: true,
|
||||||
},
|
},
|
||||||
LOG2: {
|
LOG2: {
|
||||||
execute: makeLog(2),
|
execute: makeLog(2),
|
||||||
@ -824,6 +842,7 @@ func NewFrontierInstructionSet() [256]operation {
|
|||||||
validateStack: makeStackFunc(4, 0),
|
validateStack: makeStackFunc(4, 0),
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
writes: true,
|
||||||
},
|
},
|
||||||
LOG3: {
|
LOG3: {
|
||||||
execute: makeLog(3),
|
execute: makeLog(3),
|
||||||
@ -831,6 +850,7 @@ func NewFrontierInstructionSet() [256]operation {
|
|||||||
validateStack: makeStackFunc(5, 0),
|
validateStack: makeStackFunc(5, 0),
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
writes: true,
|
||||||
},
|
},
|
||||||
LOG4: {
|
LOG4: {
|
||||||
execute: makeLog(4),
|
execute: makeLog(4),
|
||||||
@ -838,6 +858,7 @@ func NewFrontierInstructionSet() [256]operation {
|
|||||||
validateStack: makeStackFunc(6, 0),
|
validateStack: makeStackFunc(6, 0),
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
writes: true,
|
||||||
},
|
},
|
||||||
CREATE: {
|
CREATE: {
|
||||||
execute: opCreate,
|
execute: opCreate,
|
||||||
|
@ -74,6 +74,13 @@ func memoryDelegateCall(stack *Stack) *big.Int {
|
|||||||
return math.BigMax(x, y)
|
return math.BigMax(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memoryStaticCall(stack *Stack) *big.Int {
|
||||||
|
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||||
|
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||||
|
|
||||||
|
return math.BigMax(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
func memoryReturn(stack *Stack) *big.Int {
|
func memoryReturn(stack *Stack) *big.Int {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||||
}
|
}
|
||||||
|
@ -201,6 +201,7 @@ const (
|
|||||||
CALLCODE
|
CALLCODE
|
||||||
RETURN
|
RETURN
|
||||||
DELEGATECALL
|
DELEGATECALL
|
||||||
|
STATICCALL = 0xfa
|
||||||
|
|
||||||
SELFDESTRUCT = 0xff
|
SELFDESTRUCT = 0xff
|
||||||
)
|
)
|
||||||
@ -355,6 +356,7 @@ var opCodeToString = map[OpCode]string{
|
|||||||
RETURN: "RETURN",
|
RETURN: "RETURN",
|
||||||
CALLCODE: "CALLCODE",
|
CALLCODE: "CALLCODE",
|
||||||
DELEGATECALL: "DELEGATECALL",
|
DELEGATECALL: "DELEGATECALL",
|
||||||
|
STATICCALL: "STATICCALL",
|
||||||
SELFDESTRUCT: "SELFDESTRUCT",
|
SELFDESTRUCT: "SELFDESTRUCT",
|
||||||
|
|
||||||
PUSH: "PUSH",
|
PUSH: "PUSH",
|
||||||
@ -405,6 +407,7 @@ var stringToOp = map[string]OpCode{
|
|||||||
"CALLDATASIZE": CALLDATASIZE,
|
"CALLDATASIZE": CALLDATASIZE,
|
||||||
"CALLDATACOPY": CALLDATACOPY,
|
"CALLDATACOPY": CALLDATACOPY,
|
||||||
"DELEGATECALL": DELEGATECALL,
|
"DELEGATECALL": DELEGATECALL,
|
||||||
|
"STATICCALL": STATICCALL,
|
||||||
"CODESIZE": CODESIZE,
|
"CODESIZE": CODESIZE,
|
||||||
"CODECOPY": CODECOPY,
|
"CODECOPY": CODECOPY,
|
||||||
"GASPRICE": GASPRICE,
|
"GASPRICE": GASPRICE,
|
||||||
|
Loading…
Reference in New Issue
Block a user