core/state, core/vm: Nyota contract create init simplification (#30409)

Implementation of [this EIP-4762
update](https://github.com/ethereum/EIPs/pull/8867).

---------

Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Co-authored-by: Tanishq Jasoria <jasoriatanishq@gmail.com>
pull/30431/head
Guillaume Ballet 2 months ago committed by GitHub
parent c0b5d428a9
commit 9be2e010c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 14
      core/state/access_events.go
  2. 6
      core/state/access_events_test.go
  3. 2
      core/tracing/hooks.go
  4. 35
      core/vm/evm.go

@ -117,11 +117,23 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address)
return gas return gas
} }
// ContractCreateCPreheck charges access costs before
// a contract creation is initiated. It is just reads, because the
// address collision is done before the transfer, and so no write
// are guaranteed to happen at this point.
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
var gas uint64
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
return gas
}
// ContractCreateInitGas returns the access gas costs for the initialization of // ContractCreateInitGas returns the access gas costs for the initialization of
// a contract creation. // a contract creation.
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 { func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
var gas uint64 var gas uint64
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true) gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
return gas return gas
} }

@ -100,13 +100,13 @@ func TestContractCreateInitGas(t *testing.T) {
} }
// Check cold read cost, without a value // Check cold read cost, without a value
gas := ae.ContractCreateInitGas(testAddr, false) gas := ae.ContractCreateInitGas(testAddr)
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want { if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
} }
// Check warm read cost // Check warm read cost
gas = ae.ContractCreateInitGas(testAddr, false) gas = ae.ContractCreateInitGas(testAddr)
if gas != 0 { if gas != 0 {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
} }

@ -306,6 +306,8 @@ const (
GasChangeWitnessContractCreation GasChangeReason = 16 GasChangeWitnessContractCreation GasChangeReason = 16
// GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks // GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks
GasChangeWitnessCodeChunk GasChangeReason = 17 GasChangeWitnessCodeChunk GasChangeReason = 17
// GasChangeWitnessContractCollisionCheck the amount charged for checking a contract collision
GasChangeWitnessContractCollisionCheck GasChangeReason = 17
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
// it will be "manually" tracked by a direct emit of the gas change event. // it will be "manually" tracked by a direct emit of the gas change event.

@ -448,6 +448,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
} }
evm.StateDB.SetNonce(caller.Address(), nonce+1) evm.StateDB.SetNonce(caller.Address(), nonce+1)
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address)
if statelessGas > gas {
return nil, common.Address{}, 0, ErrOutOfGas
}
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
}
gas = gas - statelessGas
}
// We add this to the access list _before_ taking a snapshot. Even if the // We add this to the access list _before_ taking a snapshot. Even if the
// creation fails, the access-list change should not be rolled back. // creation fails, the access-list change should not be rolled back.
if evm.chainRules.IsEIP2929 { if evm.chainRules.IsEIP2929 {
@ -484,6 +496,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
evm.StateDB.SetNonce(address, 1) evm.StateDB.SetNonce(address, 1)
} }
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
statelessGas := evm.AccessEvents.ContractCreateInitGas(address)
if statelessGas > gas {
return nil, common.Address{}, 0, ErrOutOfGas
}
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractInit)
}
gas = gas - statelessGas
}
evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
@ -505,13 +528,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// initNewContract runs a new contract's creation code, performs checks on the // initNewContract runs a new contract's creation code, performs checks on the
// resulting code that is to be deployed, and consumes necessary gas. // resulting code that is to be deployed, and consumes necessary gas.
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) { func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) {
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
return nil, ErrOutOfGas
}
}
ret, err := evm.interpreter.Run(contract, nil, false) ret, err := evm.interpreter.Run(contract, nil, false)
if err != nil { if err != nil {
return ret, err return ret, err
@ -533,11 +549,6 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu
return ret, ErrCodeStoreOutOfGas return ret, ErrCodeStoreOutOfGas
} }
} else { } else {
// Contract creation completed, touch the missing fields in the contract
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
return ret, ErrCodeStoreOutOfGas
}
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
return ret, ErrCodeStoreOutOfGas return ret, ErrCodeStoreOutOfGas
} }

Loading…
Cancel
Save