core/vm: avoid unnecessary stack memory copies during gas calc

This commit is contained in:
Péter Szilágyi 2017-11-27 14:43:35 +02:00
parent 0f7fbb85d6
commit 8622119625
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
2 changed files with 29 additions and 17 deletions

View file

@ -352,7 +352,7 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
// We replace the stack item so that it's available when the opCall instruction is
// called. This information is otherwise lost due to the dependency on *current*
// available gas.
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
stack.data[stack.len()-1].SetUint64(cg)
if gas, overflow = math.SafeAdd(gas, cg); overflow {
return 0, errGasUintOverflow
@ -384,7 +384,7 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
// We replace the stack item so that it's available when the opCall instruction is
// called. This information is otherwise lost due to the dependency on *current*
// available gas.
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
stack.data[stack.len()-1].SetUint64(cg)
if gas, overflow = math.SafeAdd(gas, cg); overflow {
return 0, errGasUintOverflow
@ -445,7 +445,7 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
// (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)
stack.data[stack.len()-1].SetUint64(cg)
if gas, overflow = math.SafeAdd(gas, cg); overflow {
return 0, errGasUintOverflow
@ -472,7 +472,7 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
// (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)
stack.data[stack.len()-1].SetUint64(cg)
if gas, overflow = math.SafeAdd(gas, cg); overflow {
return 0, errGasUintOverflow

View file

@ -18,6 +18,7 @@ package vm
import (
"fmt"
"math/big"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
@ -137,17 +138,18 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
// to be uint256. Practically much less so feasible.
pc = uint64(0) // program counter
cost uint64
// copies used by tracer
stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule
pcCopy uint64 // needed for the deferred Tracer
gasCopy uint64 // for Tracer to log gas remaining before execution
logged bool // deferred Tracer should ignore already logged steps
pcCopy uint64 // needed for the deferred Tracer
gasCopy uint64 // for Tracer to log gas remaining before execution
stackTop *big.Int // top stack item in case CALL* gas calculation modifies it
logged bool // deferred Tracer should ignore already logged steps
)
contract.Input = input
defer func() {
if err != nil && !logged && in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
}
}()
@ -159,16 +161,20 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
// Get the memory location of pc
op = contract.GetOp(pc)
// If the transaction is being traced, track some execution metadata
if in.cfg.Debug {
logged = false
pcCopy = pc
gasCopy = contract.Gas
stackCopy = newstack()
for _, val := range stack.data {
stackCopy.push(val)
stackTop = nil
if op == CALL || op == CALLCODE || op == DELEGATECALL || op == STATICCALL {
if len(stack.data) > 0 {
stackTop = new(big.Int).Set(stack.data[stack.len()-1])
}
}
}
// Get the operation from the jump table matching the opcode and validate the
// stack and make sure there enough stack items available to perform the operation
operation := in.cfg.JumpTable[op]
@ -209,12 +215,19 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
if memorySize > 0 {
mem.Resize(memorySize)
}
// If the transaction is being traced, feed the opcode to the tracer before exec
if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
// The top stack item is modified for CALL*, temporarily replace if done so
if stackTop != nil {
stackTop, stack.data[stack.len()-1] = stack.data[stack.len()-1], stackTop
}
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
if stackTop != nil {
stackTop, stack.data[stack.len()-1] = stack.data[stack.len()-1], stackTop
}
logged = true
}
// execute the operation
res, err := operation.execute(&pc, in.evm, contract, mem, stack)
// verifyPool is a build flag. Pool verification makes sure the integrity
@ -227,7 +240,6 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
if operation.returns {
in.returnData = res
}
switch {
case err != nil:
return nil, err