all: add eip-7907 base without db table

Co-authored-by: lightclient <lightclient@proton.com>
Co-authored-by: Qi Zhou <qizhou@ethstorage.io>
This commit is contained in:
lightclient 2025-06-10 13:45:15 +02:00
parent b62c0c67fa
commit 1dc22c64ec
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
12 changed files with 223 additions and 8 deletions

View file

@ -26,8 +26,9 @@ import (
)
type accessList struct {
addresses map[common.Address]int
slots []map[common.Hash]struct{}
addresses map[common.Address]int
slots []map[common.Hash]struct{}
addressCodes map[common.Address]struct{}
}
// ContainsAddress returns true if the address is in the access list.
@ -36,6 +37,12 @@ func (al *accessList) ContainsAddress(address common.Address) bool {
return ok
}
// ContainsAddress returns true if the address is in the access list.
func (al *accessList) ContainsAddressCode(address common.Address) bool {
_, ok := al.addressCodes[address]
return ok
}
// Contains checks if a slot within an account is present in the access list, returning
// separate flags for the presence of the account and the slot respectively.
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
@ -67,6 +74,10 @@ func (al *accessList) Copy() *accessList {
for i, slotMap := range al.slots {
cp.slots[i] = maps.Clone(slotMap)
}
cp.addressCodes = make(map[common.Address]struct{}, len(al.addressCodes))
for addr := range al.addressCodes {
cp.addressCodes[addr] = struct{}{}
}
return cp
}
@ -80,6 +91,16 @@ func (al *accessList) AddAddress(address common.Address) bool {
return true
}
// AddAddressCode adds an address code to the access list, and returns 'true' if
// the operation caused a change (addr was not previously in the list).
func (al *accessList) AddAddressCode(address common.Address) bool {
if _, present := al.addressCodes[address]; present {
return false
}
al.addressCodes[address] = struct{}{}
return true
}
// AddSlot adds the specified (addr, slot) combo to the access list.
// Return values are:
// - address added
@ -142,6 +163,11 @@ func (al *accessList) Equal(other *accessList) bool {
return slices.EqualFunc(al.slots, other.slots, maps.Equal)
}
// DeleteAddressCode removes an address code from the access list.
func (al *accessList) DeleteAddressCode(address common.Address) {
delete(al.addresses, address)
}
// PrettyPrint prints the contents of the access list in a human-readable form
func (al *accessList) PrettyPrint() string {
out := new(strings.Builder)

View file

@ -221,6 +221,10 @@ func (j *journal) accessListAddAccount(addr common.Address) {
j.append(accessListAddAccountChange{addr})
}
func (j *journal) accessListAddAccountCode(addr common.Address) {
j.append(accessListAddAccountCodeChange{addr})
}
func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) {
j.append(accessListAddSlotChange{
address: addr,
@ -282,6 +286,9 @@ type (
address common.Address
slot common.Hash
}
accessListAddAccountCodeChange struct {
address common.Address
}
// Changes to transient storage
transientStorageChange struct {
@ -485,6 +492,20 @@ func (ch accessListAddAccountChange) copy() journalEntry {
}
}
func (ch accessListAddAccountCodeChange) revert(s *StateDB) {
s.accessList.DeleteAddressCode(ch.address)
}
func (ch accessListAddAccountCodeChange) dirtied() *common.Address {
return nil
}
func (ch accessListAddAccountCodeChange) copy() journalEntry {
return accessListAddAccountCodeChange{
address: ch.address,
}
}
func (ch accessListAddSlotChange) revert(s *StateDB) {
s.accessList.DeleteSlot(ch.address, ch.slot)
}

View file

@ -140,6 +140,7 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash)
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
return cached, nil
}
code, err := r.Code(addr, codeHash)
if err != nil {
return 0, err

View file

@ -1402,11 +1402,23 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
}
}
// AddAddressCodeToAccessList adds the given address to the access list
func (s *StateDB) AddAddressCodeToAccessList(addr common.Address) {
if s.accessList.AddAddressCode(addr) {
s.journal.accessListAddAccountCode(addr)
}
}
// AddressInAccessList returns true if the given address is in the access list.
func (s *StateDB) AddressInAccessList(addr common.Address) bool {
return s.accessList.ContainsAddress(addr)
}
// AddressCodeInAccessList returns true if the given address code is in the access list.
func (s *StateDB) AddressCodeInAccessList(addr common.Address) bool {
return s.accessList.ContainsAddressCode(addr)
}
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
return s.accessList.Contains(addr, slot)

View file

@ -125,10 +125,18 @@ func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash)
return s.inner.SlotInAccessList(addr, slot)
}
func (s *hookedStateDB) AddressCodeInAccessList(addr common.Address) bool {
return s.inner.AddressCodeInAccessList(addr)
}
func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) {
s.inner.AddAddressToAccessList(addr)
}
func (s *hookedStateDB) AddAddressCodeToAccessList(addr common.Address) {
s.inner.AddAddressCodeToAccessList(addr)
}
func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
s.inner.AddSlotToAccessList(addr, slot)
}

View file

@ -125,7 +125,7 @@ func TestStateProcessorErrors(t *testing.T) {
},
}
blockchain, _ = NewBlockChain(db, gspec, beacon.New(ethash.NewFaker()), nil)
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
tooBigInitCode = [params.MaxInitCodeSizeEIP3860 + 1]byte{}
)
defer blockchain.Stop()

View file

@ -41,6 +41,7 @@ var activators = map[int]func(*JumpTable){
1153: enable1153,
4762: enable4762,
7702: enable7702,
7907: enable7907,
}
// EnableEIP enables the given EIP on the config.
@ -538,3 +539,12 @@ func enable7702(jt *JumpTable) {
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
}
// enable7907 the EIP-7907 changes to support large contracts.
func enable7907(jt *JumpTable) {
jt[CALL].dynamicGas = gasCallEIP7907
jt[CALLCODE].dynamicGas = gasCallCodeEIP7907
jt[STATICCALL].dynamicGas = gasStaticCallEIP7907
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7907
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP7907
}

View file

@ -496,6 +496,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
}
gas = gas - consumed
}
// The contract code is added to the access list _after_ the contract code is successfully deployed.
if evm.chainRules.IsOsaka {
evm.StateDB.AddAddressCodeToAccessList(address)
}
evm.Context.Transfer(evm.StateDB, caller, address, value)
// Initialise a new contract and set the code that is to be used by the EVM.
@ -526,7 +530,10 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
}
// Check whether the max code size has been exceeded, assign err if the case.
if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSizeEIP170 {
return ret, ErrMaxCodeSizeExceeded
}
if evm.chainRules.IsOsaka && len(ret) > params.MaxCodeSizeEIP7907 {
return ret, ErrMaxCodeSizeExceeded
}

View file

@ -314,7 +314,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if overflow {
return 0, ErrGasUintOverflow
}
if size > params.MaxInitCodeSize {
if size > params.MaxInitCodeSizeEIP3860 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
}
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
@ -333,7 +333,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if overflow {
return 0, ErrGasUintOverflow
}
if size > params.MaxInitCodeSize {
if size > params.MaxInitCodeSizeEIP3860 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
}
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow

View file

@ -76,6 +76,7 @@ type StateDB interface {
Empty(common.Address) bool
AddressInAccessList(addr common.Address) bool
AddressCodeInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
@ -83,6 +84,9 @@ type StateDB interface {
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash)
// AddAddressCodeToAccessList adds the given address code to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddAddressCodeToAccessList(addr common.Address)
// PointCache returns the point cache used in computations
PointCache() *utils.PointCache

View file

@ -310,3 +310,127 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return total, nil
}
}
var (
gasCallEIP7907 = makeCallVariantGasCallEIP7907(gasCall)
gasDelegateCallEIP7907 = makeCallVariantGasCallEIP7907(gasDelegateCall)
gasStaticCallEIP7907 = makeCallVariantGasCallEIP7907(gasStaticCall)
gasCallCodeEIP7907 = makeCallVariantGasCallEIP7907(gasCallCode)
)
func getColdCodeAccessGasCost(evm *EVM, addr common.Address) uint64 {
codeSize := evm.StateDB.GetCodeSize(addr)
if codeSize <= params.MaxCodeSizeEIP7907 {
return 0
}
return (uint64(codeSize) - params.MaxCodeSizeEIP7907) * 2 / 32
}
func makeCallVariantGasCallEIP7907(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, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += coldCost
}
// Check code presence in the access list
if !evm.StateDB.AddressCodeInAccessList(addr) {
cost := getColdCodeAccessGasCost(evm, addr)
evm.StateDB.AddAddressCodeToAccessList(addr)
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
}
// 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, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
if !evm.StateDB.AddressCodeInAccessList(target) {
cost = getColdCodeAccessGasCost(evm, target)
evm.StateDB.AddAddressCodeToAccessList(target)
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
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
}
}
func gasExtCodeCopyEIP7907(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
var total uint64
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
// We charge (cold-warm), since 'warm' is already charged as constantGas
if !contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += gas
}
// Check address code presence in the access list
if !evm.StateDB.AddressCodeInAccessList(addr) {
cost := getColdCodeAccessGasCost(evm, addr)
evm.StateDB.AddAddressCodeToAccessList(addr)
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
}
return total, nil
}

View file

@ -132,8 +132,10 @@ const (
DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
MaxCodeSizeEIP170 = 24576 // Maximum bytecode to permit for a contract
MaxCodeSizeEIP7907 = 268435456 // Maximum bytecode permitted per contract after EIP-7907
MaxInitCodeSizeEIP3860 = 2 * MaxCodeSizeEIP170 // Maximum initcode to permit in a creation transaction and create instructions
MaxInitCodeSizeEIP7903 = 2 * MaxCodeSizeEIP170 // Maximum initcode to permit in a creation transaction and create instructions
// Precompiled contract gas prices