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. // 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
} }
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 { 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 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 {
mem.Resize(memorySize)
}
} }
if memorySize > 0 {
mem.Resize(memorySize)
}
if in.cfg.Debug { if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) in.cfg.Tracer.CaptureState(in.evm, 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 {

View file

@ -17,6 +17,8 @@
package vm package vm
import ( import (
"fmt"
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
) )
@ -60,16 +62,34 @@ 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 %#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 { func newEip1559InstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet() instructionSet := newShanghaiInstructionSet()
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
return instructionSet return validate(instructionSet)
} }
func newShanghaiInstructionSet() JumpTable { func newShanghaiInstructionSet() JumpTable {
instructionSet := newMergeInstructionSet() instructionSet := newMergeInstructionSet()
enable3855(&instructionSet) // PUSH0 instruction enable3855(&instructionSet) // PUSH0 instruction
return instructionSet return validate(instructionSet)
} }
func newMergeInstructionSet() JumpTable { func newMergeInstructionSet() JumpTable {
@ -80,7 +100,7 @@ func newMergeInstructionSet() JumpTable {
minStack: minStack(0, 1), minStack: minStack(0, 1),
maxStack: maxStack(0, 1), maxStack: maxStack(0, 1),
} }
return instructionSet return validate(instructionSet)
} }
// newLondonInstructionSet returns the frontier, homestead, byzantium, // newLondonInstructionSet returns the frontier, homestead, byzantium,
@ -89,7 +109,7 @@ 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,
@ -97,7 +117,7 @@ func newLondonInstructionSet() JumpTable {
func newBerlinInstructionSet() JumpTable { func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet() instructionSet := newIstanbulInstructionSet()
// enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 // 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 // newIstanbulInstructionSet returns the frontier, homestead
@ -109,7 +129,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
@ -148,7 +168,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
@ -184,14 +204,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)
} }
@ -205,7 +225,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
@ -220,7 +240,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
@ -1036,5 +1056,5 @@ func newFrontierInstructionSet() JumpTable {
} }
} }
return tbl return validate(tbl)
} }