aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2017-08-15 16:23:23 +0800
committerPéter Szilágyi <peterke@gmail.com>2017-08-15 18:03:49 +0800
commit3d123bcde67973b57c0a9e7edc219cc2ea589443 (patch)
treee806ab2faae07184fdcdfe23488b7111e75a9057 /core
parent9facf6423dbd38ebd7fbd9069cbcb98b0fd243c2 (diff)
downloadgo-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar.gz
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar.bz2
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar.lz
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar.xz
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.tar.zst
go-tangerine-3d123bcde67973b57c0a9e7edc219cc2ea589443.zip
core/vm: implement metropolis static call opcode
Diffstat (limited to 'core')
-rw-r--r--core/vm/evm.go45
-rw-r--r--core/vm/gas_table.go27
-rw-r--r--core/vm/instructions.go33
-rw-r--r--core/vm/interpreter.go16
-rw-r--r--core/vm/jump_table.go25
-rw-r--r--core/vm/memory_table.go7
-rw-r--r--core/vm/opcodes.go3
7 files changed, 153 insertions, 3 deletions
diff --git a/core/vm/evm.go b/core/vm/evm.go
index b8af9bd15..97a9d3105 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -245,6 +245,51 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
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.
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 {
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 761ca4450..8a6c2741d 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -423,6 +423,33 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
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) {
return GasFastestStep, nil
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 4f9e45ffe..aaa8d7945 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,6 +17,7 @@
package vm
import (
+ "errors"
"fmt"
"math/big"
@@ -28,7 +29,8 @@ import (
)
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) {
@@ -656,6 +658,35 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
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) {
offset, size := stack.pop(), stack.pop()
ret := memory.GetPtr(offset.Int64(), size.Int64())
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 3faa98704..32d764b9f 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -69,6 +69,8 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
// we'll set the default jump table.
if !cfg.JumpTable[STOP].valid {
switch {
+ case evm.ChainConfig().IsMetropolis(evm.BlockNumber):
+ cfg.JumpTable = metropolisInstructionSet
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
cfg.JumpTable = homesteadInstructionSet
default:
@@ -85,6 +87,19 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
}
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
}
@@ -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
// 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) {
+ // Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 0034eacb7..f6e8dae66 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -56,10 +56,26 @@ type operation struct {
}
var (
- frontierInstructionSet = NewFrontierInstructionSet()
- homesteadInstructionSet = NewHomesteadInstructionSet()
+ frontierInstructionSet = NewFrontierInstructionSet()
+ 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
// instructions that can be executed during the homestead phase.
func NewHomesteadInstructionSet() [256]operation {
@@ -810,6 +826,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(2, 0),
memorySize: memoryLog,
valid: true,
+ writes: true,
},
LOG1: {
execute: makeLog(1),
@@ -817,6 +834,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(3, 0),
memorySize: memoryLog,
valid: true,
+ writes: true,
},
LOG2: {
execute: makeLog(2),
@@ -824,6 +842,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(4, 0),
memorySize: memoryLog,
valid: true,
+ writes: true,
},
LOG3: {
execute: makeLog(3),
@@ -831,6 +850,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(5, 0),
memorySize: memoryLog,
valid: true,
+ writes: true,
},
LOG4: {
execute: makeLog(4),
@@ -838,6 +858,7 @@ func NewFrontierInstructionSet() [256]operation {
validateStack: makeStackFunc(6, 0),
memorySize: memoryLog,
valid: true,
+ writes: true,
},
CREATE: {
execute: opCreate,
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 654137c70..9d293a2f2 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -74,6 +74,13 @@ func memoryDelegateCall(stack *Stack) *big.Int {
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 {
return calcMemSize(stack.Back(0), stack.Back(1))
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index d4ba7f156..51925e8dd 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -201,6 +201,7 @@ const (
CALLCODE
RETURN
DELEGATECALL
+ STATICCALL = 0xfa
SELFDESTRUCT = 0xff
)
@@ -355,6 +356,7 @@ var opCodeToString = map[OpCode]string{
RETURN: "RETURN",
CALLCODE: "CALLCODE",
DELEGATECALL: "DELEGATECALL",
+ STATICCALL: "STATICCALL",
SELFDESTRUCT: "SELFDESTRUCT",
PUSH: "PUSH",
@@ -405,6 +407,7 @@ var stringToOp = map[string]OpCode{
"CALLDATASIZE": CALLDATASIZE,
"CALLDATACOPY": CALLDATACOPY,
"DELEGATECALL": DELEGATECALL,
+ "STATICCALL": STATICCALL,
"CODESIZE": CODESIZE,
"CODECOPY": CODECOPY,
"GASPRICE": GASPRICE,