core/vm: avoid memory expansion check for trivial ops (#24048)

This commit is contained in:
Daniel Liu 2024-09-24 15:04:08 +08:00 committed by Daniel Liu
parent a401d3fd3f
commit 224caa5a08
2 changed files with 57 additions and 41 deletions

View file

@ -219,54 +219,50 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
// Static portion of gas
cost = operation.constantGas // For tracing
if !contract.UseGas(operation.constantGas) {
if !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
var memorySize uint64
// calculate the new memory size and expand the memory to fit
// the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
return nil, ErrGasUintOverflow
}
}
// Dynamic portion of gas
// 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
if operation.dynamicGas != nil {
// All ops with a dynamic memory usage also has a dynamic gas cost.
var memorySize uint64
// calculate the new memory size and expand the memory to fit
// the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
return nil, ErrGasUintOverflow
}
}
// 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
var dynamicCost uint64
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) {
return nil, ErrOutOfGas
}
if memorySize > 0 {
mem.Resize(memorySize)
}
}
if memorySize > 0 {
mem.Resize(memorySize)
}
if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
// execute the operation
res, err = operation.execute(&pc, in, callContext)
if err != nil {

View file

@ -17,6 +17,8 @@
package vm
import (
"fmt"
"github.com/XinFinOrg/XDPoSChain/params"
)
@ -60,16 +62,34 @@ var (
// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation
func validate(jt JumpTable) JumpTable {
for i, op := range jt {
if op == nil {
panic(fmt.Sprintf("op %#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
}
func newEip1559InstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
return instructionSet
return validate(instructionSet)
}
func newShanghaiInstructionSet() JumpTable {
instructionSet := newMergeInstructionSet()
enable3855(&instructionSet) // PUSH0 instruction
return instructionSet
return validate(instructionSet)
}
func newMergeInstructionSet() JumpTable {
@ -80,7 +100,7 @@ func newMergeInstructionSet() JumpTable {
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
return instructionSet
return validate(instructionSet)
}
// newLondonInstructionSet returns the frontier, homestead, byzantium,
@ -89,7 +109,7 @@ func newLondonInstructionSet() JumpTable {
instructionSet := newBerlinInstructionSet()
// 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
return instructionSet
return validate(instructionSet)
}
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
@ -97,7 +117,7 @@ func newLondonInstructionSet() JumpTable {
func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet()
// enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
return instructionSet
return validate(instructionSet)
}
// newIstanbulInstructionSet returns the frontier, homestead
@ -109,7 +129,7 @@ func newIstanbulInstructionSet() JumpTable {
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
return instructionSet
return validate(instructionSet)
}
// newConstantinopleInstructionSet returns the frontier, homestead
@ -148,7 +168,7 @@ func newConstantinopleInstructionSet() JumpTable {
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
}
return instructionSet
return validate(instructionSet)
}
// newByzantiumInstructionSet returns the frontier, homestead and
@ -184,14 +204,14 @@ func newByzantiumInstructionSet() JumpTable {
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
}
return instructionSet
return validate(instructionSet)
}
// EIP 158 a.k.a Spurious Dragon
func newSpuriousDragonInstructionSet() JumpTable {
instructionSet := newTangerineWhistleInstructionSet()
instructionSet[EXP].dynamicGas = gasExpEIP158
return instructionSet
return validate(instructionSet)
}
@ -205,7 +225,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
instructionSet[CALL].constantGas = params.CallGasEIP150
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
return instructionSet
return validate(instructionSet)
}
// newHomesteadInstructionSet returns the frontier and homestead
@ -220,7 +240,7 @@ func newHomesteadInstructionSet() JumpTable {
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
}
return instructionSet
return validate(instructionSet)
}
// newFrontierInstructionSet returns the frontier instructions
@ -1036,5 +1056,5 @@ func newFrontierInstructionSet() JumpTable {
}
}
return tbl
return validate(tbl)
}