Skip to content

Commit bc5dc7b

Browse files
committed
core/vm: avoid memory expansion check for trivial ops
1 parent b02fe53 commit bc5dc7b

File tree

2 files changed

+54
-40
lines changed

2 files changed

+54
-40
lines changed

core/vm/interpreter.go

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -182,62 +182,56 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
182182
// Capture pre-execution values for tracing.
183183
logged, pcCopy, gasCopy = false, pc, contract.Gas
184184
}
185-
186185
// Get the operation from the jump table and validate the stack to ensure there are
187186
// enough stack items available to perform the operation.
188187
op = contract.GetOp(pc)
189188
operation := in.cfg.JumpTable[op]
189+
cost = operation.constantGas // For tracing
190190
// Validate stack
191191
if sLen := stack.len(); sLen < operation.minStack {
192192
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
193193
} else if sLen > operation.maxStack {
194194
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
195195
}
196-
// Static portion of gas
197-
cost = operation.constantGas // For tracing
198-
if !contract.UseGas(operation.constantGas) {
196+
if !contract.UseGas(cost) {
199197
return nil, ErrOutOfGas
200198
}
201-
202-
var memorySize uint64
203-
// calculate the new memory size and expand the memory to fit
204-
// the operation
205-
// Memory check needs to be done prior to evaluating the dynamic gas portion,
206-
// to detect calculation overflows
207-
if operation.memorySize != nil {
208-
memSize, overflow := operation.memorySize(stack)
209-
if overflow {
210-
return nil, ErrGasUintOverflow
211-
}
212-
// memory is expanded in words of 32 bytes. Gas
213-
// is also calculated in words.
214-
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
215-
return nil, ErrGasUintOverflow
216-
}
217-
}
218-
// Dynamic portion of gas
219-
// consume the gas and return an error if not enough gas is available.
220-
// cost is explicitly set so that the capture state defer method can get the proper cost
221199
if operation.dynamicGas != nil {
200+
// All ops with a dynamic memory usage also has a dynamic gas cost.
201+
var memorySize uint64
202+
// calculate the new memory size and expand the memory to fit
203+
// the operation
204+
// Memory check needs to be done prior to evaluating the dynamic gas portion,
205+
// to detect calculation overflows
206+
if operation.memorySize != nil {
207+
memSize, overflow := operation.memorySize(stack)
208+
if overflow {
209+
return nil, ErrGasUintOverflow
210+
}
211+
// memory is expanded in words of 32 bytes. Gas
212+
// is also calculated in words.
213+
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
214+
return nil, ErrGasUintOverflow
215+
}
216+
}
217+
// Consume the gas and return an error if not enough gas is available.
218+
// cost is explicitly set so that the capture state defer method can get the proper cost
222219
var dynamicCost uint64
223220
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
224-
cost += dynamicCost // total cost, for debug tracing
221+
cost += dynamicCost // for tracing
225222
if err != nil || !contract.UseGas(dynamicCost) {
226223
return nil, ErrOutOfGas
227224
}
225+
if memorySize > 0 {
226+
mem.Resize(memorySize)
227+
}
228228
}
229-
if memorySize > 0 {
230-
mem.Resize(memorySize)
231-
}
232-
233229
if in.cfg.Debug {
234230
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
235231
logged = true
236232
}
237-
238233
// execute the operation
239234
res, err = operation.execute(&pc, in, callContext)
240-
241235
if err != nil {
242236
break
243237
}

core/vm/jump_table.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package vm
1818

1919
import (
20+
"fmt"
21+
2022
"github.com/ethereum/go-ethereum/params"
2123
)
2224

@@ -57,21 +59,39 @@ var (
5759
// JumpTable contains the EVM opcodes supported at a given fork.
5860
type JumpTable [256]*operation
5961

62+
func validate(jt JumpTable) JumpTable {
63+
for i, op := range jt {
64+
if op == nil {
65+
panic(fmt.Sprintf("op 0x%x is not set", i))
66+
}
67+
// The interpreter has an assumption that if the memorySize function is
68+
// set, then the dynamicGas function is also set. This is a somewhat
69+
// arbitrary assumption, and can be removed if we need to -- but it
70+
// allows us to avoid a condition check. As long as we have that assumption
71+
// in there, this little sanity check prevents us from merging in a
72+
// change which violates it.
73+
if op.memorySize != nil && op.dynamicGas == nil {
74+
panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
75+
}
76+
}
77+
return jt
78+
}
79+
6080
// newLondonInstructionSet returns the frontier, homestead, byzantium,
6181
// contantinople, istanbul, petersburg, berlin and london instructions.
6282
func newLondonInstructionSet() JumpTable {
6383
instructionSet := newBerlinInstructionSet()
6484
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
6585
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
66-
return instructionSet
86+
return validate(instructionSet)
6787
}
6888

6989
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
7090
// contantinople, istanbul, petersburg and berlin instructions.
7191
func newBerlinInstructionSet() JumpTable {
7292
instructionSet := newIstanbulInstructionSet()
7393
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
74-
return instructionSet
94+
return validate(instructionSet)
7595
}
7696

7797
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
@@ -83,7 +103,7 @@ func newIstanbulInstructionSet() JumpTable {
83103
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
84104
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
85105

86-
return instructionSet
106+
return validate(instructionSet)
87107
}
88108

89109
// newConstantinopleInstructionSet returns the frontier, homestead,
@@ -122,7 +142,7 @@ func newConstantinopleInstructionSet() JumpTable {
122142
maxStack: maxStack(4, 1),
123143
memorySize: memoryCreate2,
124144
}
125-
return instructionSet
145+
return validate(instructionSet)
126146
}
127147

128148
// newByzantiumInstructionSet returns the frontier, homestead and
@@ -158,14 +178,14 @@ func newByzantiumInstructionSet() JumpTable {
158178
maxStack: maxStack(2, 0),
159179
memorySize: memoryRevert,
160180
}
161-
return instructionSet
181+
return validate(instructionSet)
162182
}
163183

164184
// EIP 158 a.k.a Spurious Dragon
165185
func newSpuriousDragonInstructionSet() JumpTable {
166186
instructionSet := newTangerineWhistleInstructionSet()
167187
instructionSet[EXP].dynamicGas = gasExpEIP158
168-
return instructionSet
188+
return validate(instructionSet)
169189

170190
}
171191

@@ -179,7 +199,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
179199
instructionSet[CALL].constantGas = params.CallGasEIP150
180200
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
181201
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
182-
return instructionSet
202+
return validate(instructionSet)
183203
}
184204

185205
// newHomesteadInstructionSet returns the frontier and homestead
@@ -194,7 +214,7 @@ func newHomesteadInstructionSet() JumpTable {
194214
maxStack: maxStack(6, 1),
195215
memorySize: memoryDelegateCall,
196216
}
197-
return instructionSet
217+
return validate(instructionSet)
198218
}
199219

200220
// newFrontierInstructionSet returns the frontier instructions
@@ -1010,5 +1030,5 @@ func newFrontierInstructionSet() JumpTable {
10101030
}
10111031
}
10121032

1013-
return tbl
1033+
return validate(tbl)
10141034
}

0 commit comments

Comments
 (0)