Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) {

chainConfig := *params.TestChainConfig
chainConfig.CancunBlock = nil
chainConfig.PragueBlock = nil
gspec := &Genesis{
Config: &chainConfig,
Alloc: GenesisAlloc{
Expand Down Expand Up @@ -2976,6 +2977,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) {

chainConfig := *params.TestChainConfig
chainConfig.CancunBlock = nil
chainConfig.PragueBlock = nil
gspec := &Genesis{
Config: &chainConfig,
Alloc: GenesisAlloc{
Expand Down Expand Up @@ -3102,6 +3104,7 @@ func testDeleteRecreateSlotsAcrossManyBlocks(t *testing.T, scheme string) {
t.Logf("Destination address: %x\n", aa)
chainConfig := *params.TestChainConfig
chainConfig.CancunBlock = nil
chainConfig.PragueBlock = nil
gspec := &Genesis{
Config: &chainConfig,
Alloc: GenesisAlloc{
Expand Down Expand Up @@ -3439,7 +3442,7 @@ func testEIP2718Transition(t *testing.T, scheme string) {

// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
if block.GasUsed() != expected {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())

Expand Down Expand Up @@ -3542,7 +3545,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {

// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
expectedGas := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
if block.GasUsed() != expectedGas {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
}
Expand Down
9 changes: 9 additions & 0 deletions core/vm/eips.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var activators = map[int]func(*JumpTable){
1884: enable1884,
1344: enable1344,
1153: enable1153,
7702: enable7702,
}

// EnableEIP enables the given EIP on the config.
Expand Down Expand Up @@ -319,3 +320,11 @@ func enable6780(jt *JumpTable) {
halts: true,
}
}

// enable7702 the EIP-7702 changes to support delegation designators.
func enable7702(jt *JumpTable) {
jt[CALL].dynamicGas = gasCallEIP7702
jt[CALLCODE].dynamicGas = gasCallCodeEIP7702
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
}
51 changes: 40 additions & 11 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,15 +295,15 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
code := evm.StateDB.GetCode(addr)
code := evm.resolveCode(addr)
if len(code) == 0 {
ret, err = nil, nil // gas is unchanged
} else {
addrCopy := addr
// If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
Expand Down Expand Up @@ -363,7 +363,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
Expand Down Expand Up @@ -421,7 +421,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
addrCopy := addr
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
}
Expand Down Expand Up @@ -477,7 +477,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
Expand Down Expand Up @@ -666,17 +666,46 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}

// resolveCode returns the code associated with the provided account. After
// Prague, it can also resolve code pointed to by a delegation designator.
func (evm *EVM) resolveCode(addr common.Address) []byte {
code := evm.StateDB.GetCode(addr)
if !evm.chainRules.IsPrague {
return code
}
if target, ok := types.ParseDelegation(code); ok {
// Note we only follow one level of delegation.
return evm.StateDB.GetCode(target)
}
return code
}

// resolveCodeHash returns the code hash associated with the provided address.
// After Prague, it can also resolve code hash of the account pointed to by a
// delegation designator. Although this is not accessible in the EVM it is used
// internally to associate jumpdest analysis to code.
func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
if evm.chainRules.IsPrague {
code := evm.StateDB.GetCode(addr)
if target, ok := types.ParseDelegation(code); ok {
// Note we only follow one level of delegation.
return evm.StateDB.GetCodeHash(target)
}
}
return evm.StateDB.GetCodeHash(addr)
}

// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }

// PublishEvent executes Publish function from OpEvent if OpCode is found in Context.PublishEvents
func (evm *EVM) PublishEvent(
opCode OpCode,
counter uint64,
from, to common.Address,
value *big.Int,
input, output []byte,
err error,
opCode OpCode,
counter uint64,
from, to common.Address,
value *big.Int,
input, output []byte,
err error,
) {
context := evm.Context
if context.CurrentTransaction == nil {
Expand Down
2 changes: 2 additions & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
if cfg.JumpTable[STOP] == nil {
var jt JumpTable
switch {
case evm.chainRules.IsPrague:
jt = pragueInstructionSet
case evm.chainRules.IsCancun:
jt = cancunInstructionSet
case evm.chainRules.IsShanghai:
Expand Down
7 changes: 7 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,18 @@ var (
londonInstructionSet = newLondonInstructionSet()
shanghaiInstructionSet = newShanghaiInstructionSet()
cancunInstructionSet = newCancunInstructionSet()
pragueInstructionSet = newPragueInstructionSet()
)

// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation

func newPragueInstructionSet() JumpTable {
instructionSet := newCancunInstructionSet()
enable7702(&instructionSet) // EIP-7702 Setcode transaction type
return instructionSet
}

func newCancunInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
Expand Down
68 changes: 68 additions & 0 deletions core/vm/operations_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)

Expand Down Expand Up @@ -247,3 +248,70 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
}
return gasFunc
}

var (
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
)

func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
total uint64 // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20())
)

// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost) {
return 0, ErrOutOfGas
}
total += coldCost
}

// Check if code is a delegation and if so, charge for resolution.
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
var cost uint64
if evm.StateDB.AddressInAccessList(target) {
cost = params.WarmStorageReadCostEIP2929
} else {
evm.StateDB.AddAddressToAccessList(target)
cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(cost) {
return 0, ErrOutOfGas
}
total += cost
}

// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
if err != nil {
return old, err
}

// Temporarily add the gas charge back to the contract and return value. By
// adding it to the return, it will be charged outside of this function, as
// part of the dynamic gas. This will ensure it is correctly reported to
// tracers.
contract.Gas += total

var overflow bool
if total, overflow = math.SafeAdd(old, total); overflow {
return 0, ErrGasUintOverflow
}
return total, nil
}
}
Loading