core/vm: avoid memory expansion check for trivial ops (#24048)
This commit is contained in:
parent
adec878c1d
commit
155795be99
@ -181,23 +181,22 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
// Capture pre-execution values for tracing.
|
// Capture pre-execution values for tracing.
|
||||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
// enough stack items available to perform the operation.
|
// enough stack items available to perform the operation.
|
||||||
op = contract.GetOp(pc)
|
op = contract.GetOp(pc)
|
||||||
operation := in.cfg.JumpTable[op]
|
operation := in.cfg.JumpTable[op]
|
||||||
|
cost = operation.constantGas // For tracing
|
||||||
// Validate stack
|
// Validate stack
|
||||||
if sLen := stack.len(); sLen < operation.minStack {
|
if sLen := stack.len(); sLen < operation.minStack {
|
||||||
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
|
||||||
} else if sLen > operation.maxStack {
|
} else if sLen > operation.maxStack {
|
||||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||||
}
|
}
|
||||||
// Static portion of gas
|
if !contract.UseGas(cost) {
|
||||||
cost = operation.constantGas // For tracing
|
|
||||||
if !contract.UseGas(operation.constantGas) {
|
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
if operation.dynamicGas != nil {
|
||||||
|
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||||
var memorySize uint64
|
var memorySize uint64
|
||||||
// calculate the new memory size and expand the memory to fit
|
// calculate the new memory size and expand the memory to fit
|
||||||
// the operation
|
// the operation
|
||||||
@ -214,29 +213,24 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
return nil, ErrGasUintOverflow
|
return nil, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dynamic portion of gas
|
// Consume the gas and return an error if not enough gas is available.
|
||||||
// consume the gas and return an error if not enough gas is available.
|
|
||||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||||
if operation.dynamicGas != nil {
|
|
||||||
var dynamicCost uint64
|
var dynamicCost uint64
|
||||||
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
||||||
cost += dynamicCost // total cost, for debug tracing
|
cost += dynamicCost // for tracing
|
||||||
if err != nil || !contract.UseGas(dynamicCost) {
|
if err != nil || !contract.UseGas(dynamicCost) {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if memorySize > 0 {
|
if memorySize > 0 {
|
||||||
mem.Resize(memorySize)
|
mem.Resize(memorySize)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if in.cfg.Debug {
|
if in.cfg.Debug {
|
||||||
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||||
logged = true
|
logged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the operation
|
// execute the operation
|
||||||
res, err = operation.execute(&pc, in, callContext)
|
res, err = operation.execute(&pc, in, callContext)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,13 +59,31 @@ var (
|
|||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
type JumpTable [256]*operation
|
type JumpTable [256]*operation
|
||||||
|
|
||||||
|
func validate(jt JumpTable) JumpTable {
|
||||||
|
for i, op := range jt {
|
||||||
|
if op == nil {
|
||||||
|
panic(fmt.Sprintf("op 0x%x is not set", i))
|
||||||
|
}
|
||||||
|
// The interpreter has an assumption that if the memorySize function is
|
||||||
|
// set, then the dynamicGas function is also set. This is a somewhat
|
||||||
|
// arbitrary assumption, and can be removed if we need to -- but it
|
||||||
|
// allows us to avoid a condition check. As long as we have that assumption
|
||||||
|
// in there, this little sanity check prevents us from merging in a
|
||||||
|
// change which violates it.
|
||||||
|
if op.memorySize != nil && op.dynamicGas == nil {
|
||||||
|
panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jt
|
||||||
|
}
|
||||||
|
|
||||||
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// contantinople, istanbul, petersburg, berlin and london instructions.
|
// contantinople, istanbul, petersburg, berlin and london instructions.
|
||||||
func newLondonInstructionSet() JumpTable {
|
func newLondonInstructionSet() JumpTable {
|
||||||
instructionSet := newBerlinInstructionSet()
|
instructionSet := newBerlinInstructionSet()
|
||||||
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
||||||
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
||||||
@ -71,7 +91,7 @@ func newLondonInstructionSet() JumpTable {
|
|||||||
func newBerlinInstructionSet() JumpTable {
|
func newBerlinInstructionSet() JumpTable {
|
||||||
instructionSet := newIstanbulInstructionSet()
|
instructionSet := newIstanbulInstructionSet()
|
||||||
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
|
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
||||||
@ -83,7 +103,7 @@ func newIstanbulInstructionSet() JumpTable {
|
|||||||
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
||||||
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
|
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
|
||||||
|
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newConstantinopleInstructionSet returns the frontier, homestead,
|
// newConstantinopleInstructionSet returns the frontier, homestead,
|
||||||
@ -122,7 +142,7 @@ func newConstantinopleInstructionSet() JumpTable {
|
|||||||
maxStack: maxStack(4, 1),
|
maxStack: maxStack(4, 1),
|
||||||
memorySize: memoryCreate2,
|
memorySize: memoryCreate2,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newByzantiumInstructionSet returns the frontier, homestead and
|
// newByzantiumInstructionSet returns the frontier, homestead and
|
||||||
@ -158,14 +178,14 @@ func newByzantiumInstructionSet() JumpTable {
|
|||||||
maxStack: maxStack(2, 0),
|
maxStack: maxStack(2, 0),
|
||||||
memorySize: memoryRevert,
|
memorySize: memoryRevert,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP 158 a.k.a Spurious Dragon
|
// EIP 158 a.k.a Spurious Dragon
|
||||||
func newSpuriousDragonInstructionSet() JumpTable {
|
func newSpuriousDragonInstructionSet() JumpTable {
|
||||||
instructionSet := newTangerineWhistleInstructionSet()
|
instructionSet := newTangerineWhistleInstructionSet()
|
||||||
instructionSet[EXP].dynamicGas = gasExpEIP158
|
instructionSet[EXP].dynamicGas = gasExpEIP158
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +199,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
|
|||||||
instructionSet[CALL].constantGas = params.CallGasEIP150
|
instructionSet[CALL].constantGas = params.CallGasEIP150
|
||||||
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
|
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
|
||||||
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
|
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHomesteadInstructionSet returns the frontier and homestead
|
// newHomesteadInstructionSet returns the frontier and homestead
|
||||||
@ -194,7 +214,7 @@ func newHomesteadInstructionSet() JumpTable {
|
|||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
memorySize: memoryDelegateCall,
|
memorySize: memoryDelegateCall,
|
||||||
}
|
}
|
||||||
return instructionSet
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFrontierInstructionSet returns the frontier instructions
|
// newFrontierInstructionSet returns the frontier instructions
|
||||||
@ -1010,5 +1030,5 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tbl
|
return validate(tbl)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user