Skip to content

Commit

Permalink
core/vm: avoid memory expansion check for trivial ops (#6383)
Browse files Browse the repository at this point in the history
Cherry-pick ethereum/go-ethereum#24048

Co-authored-by: Martin Holst Swende <martin@swende.se>
  • Loading branch information
yperbasis and holiman committed Dec 20, 2022
1 parent 2711e3a commit 06df433
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 34 deletions.
8 changes: 4 additions & 4 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ func TestPreShanghaiBodyNoPanicOnWithdrawals(t *testing.T) {
bstring, _ := hex.DecodeString(bodyRlp)

body := new(types.Body)
rlp.DecodeBytes([]byte(bstring), body)
rlp.DecodeBytes(bstring, body)

require.Nil(body.Withdrawals)
require.Equal(2, len(body.Transactions))
Expand All @@ -578,7 +578,7 @@ func TestPreShanghaiBodyForStorageNoPanicOnWithdrawals(t *testing.T) {
bstring, _ := hex.DecodeString(bodyForStorageRlp)

body := new(types.BodyForStorage)
rlp.DecodeBytes([]byte(bstring), body)
rlp.DecodeBytes(bstring, body)

require.Nil(body.Withdrawals)
require.Equal(uint32(2), body.TxAmount)
Expand All @@ -592,7 +592,7 @@ func TestShanghaiBodyForStorageHasWithdrawals(t *testing.T) {
bstring, _ := hex.DecodeString(bodyForStorageRlp)

body := new(types.BodyForStorage)
rlp.DecodeBytes([]byte(bstring), body)
rlp.DecodeBytes(bstring, body)

require.NotNil(body.Withdrawals)
require.Equal(2, len(body.Withdrawals))
Expand All @@ -607,7 +607,7 @@ func TestShanghaiBodyForStorageNoWithdrawals(t *testing.T) {
bstring, _ := hex.DecodeString(bodyForStorageRlp)

body := new(types.BodyForStorage)
rlp.DecodeBytes([]byte(bstring), body)
rlp.DecodeBytes(bstring, body)

require.NotNil(body.Withdrawals)
require.Equal(0, len(body.Withdrawals))
Expand Down
55 changes: 25 additions & 30 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, _pc, contract.Gas
}

// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(_pc)
operation := in.jt[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := locStack.Len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
Expand All @@ -298,48 +298,43 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return nil, ErrWriteProtection
}
}
// 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(locStack)
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(locStack)
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, locStack, 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(_pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) //nolint:errcheck
logged = true
}

// execute the operation
res, err = operation.execute(pc, in, callContext)
// if the operation clears the return data (e.g. it has returning data)
Expand Down
30 changes: 30 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package vm

import (
"fmt"

"github.com/ledgerwatch/erigon/core/vm/stack"
"github.com/ledgerwatch/erigon/params"
)
Expand Down Expand Up @@ -72,11 +74,29 @@ var (
// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation

func validate(jt *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()))
}
}
}

// newCancunInstructionSet returns the frontier, homestead, byzantium,
// constantinople, istanbul, petersburg, berlin, london, paris, shanghai,
// and cancun instructions.
func newCancunInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
validate(&instructionSet)
return instructionSet
}

Expand All @@ -86,6 +106,7 @@ func newShanghaiInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet()
enable3855(&instructionSet) // PUSH0 instruction https://eips.ethereum.org/EIPS/eip-3855
enable3860(&instructionSet) // Limit and meter initcode https://eips.ethereum.org/EIPS/eip-3860
validate(&instructionSet)
return instructionSet
}

Expand All @@ -95,6 +116,7 @@ func newLondonInstructionSet() JumpTable {
instructionSet := newBerlinInstructionSet()
enable3529(&instructionSet) // Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
validate(&instructionSet)
return instructionSet
}

Expand All @@ -103,6 +125,7 @@ func newLondonInstructionSet() JumpTable {
func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet()
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
validate(&instructionSet)
return instructionSet
}

Expand All @@ -115,6 +138,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

validate(&instructionSet)
return instructionSet
}

Expand Down Expand Up @@ -166,6 +190,7 @@ func newConstantinopleInstructionSet() JumpTable {
writes: true,
returns: true,
}
validate(&instructionSet)
return instructionSet
}

Expand Down Expand Up @@ -213,13 +238,15 @@ func newByzantiumInstructionSet() JumpTable {
reverts: true,
returns: true,
}
validate(&instructionSet)
return instructionSet
}

// EIP 158 a.k.a Spurious Dragon
func newSpuriousDragonInstructionSet() JumpTable {
instructionSet := newTangerineWhistleInstructionSet()
instructionSet[EXP].dynamicGas = gasExpEIP160
validate(&instructionSet)
return instructionSet

}
Expand All @@ -234,6 +261,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
instructionSet[CALL].constantGas = params.CallGasEIP150
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
validate(&instructionSet)
return instructionSet
}

Expand All @@ -252,6 +280,7 @@ func newHomesteadInstructionSet() JumpTable {
memorySize: memoryDelegateCall,
returns: true,
}
validate(&instructionSet)
return instructionSet
}

Expand Down Expand Up @@ -1471,5 +1500,6 @@ func newFrontierInstructionSet() JumpTable {
}
}

validate(&tbl)
return tbl
}

0 comments on commit 06df433

Please sign in to comment.