Skip to content

Commit

Permalink
core/state, core/vm: Nyota contract create init simplification (#30409)
Browse files Browse the repository at this point in the history
Implementation of [this EIP-4762
update](ethereum/EIPs#8867).

---------

Signed-off-by: Guillaume Ballet <[email protected]>
Co-authored-by: Tanishq Jasoria <[email protected]>
  • Loading branch information
gballet and tanishqjasoria authored Sep 13, 2024
1 parent c0b5d42 commit 9be2e01
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 16 deletions.
14 changes: 13 additions & 1 deletion core/state/access_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,23 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address)
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
// a contract creation.
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
var gas uint64
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
return gas
}

Expand Down
6 changes: 3 additions & 3 deletions core/state/access_events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ func TestContractCreateInitGas(t *testing.T) {
}

// Check cold read cost, without a value
gas := ae.ContractCreateInitGas(testAddr, false)
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
gas := ae.ContractCreateInitGas(testAddr)
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)
}

// Check warm read cost
gas = ae.ContractCreateInitGas(testAddr, false)
gas = ae.ContractCreateInitGas(testAddr)
if gas != 0 {
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
}
Expand Down
2 changes: 2 additions & 0 deletions core/tracing/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ const (
GasChangeWitnessContractCreation GasChangeReason = 16
// GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks
GasChangeWitnessCodeChunk GasChangeReason = 17
// GasChangeWitnessContractCollisionCheck the amount charged for checking a contract collision
GasChangeWitnessContractCollisionCheck GasChangeReason = 17

This comment has been minimized.

Copy link
@holiman

holiman Sep 13, 2024

Contributor

Is this an amount or a "manual enum"? If the latter, you have used 17 twice.


// 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.
Expand Down
35 changes: 23 additions & 12 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
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
// creation fails, the access-list change should not be rolled back.
if evm.chainRules.IsEIP2929 {
Expand Down Expand Up @@ -484,6 +496,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.chainRules.IsEIP158 {
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)

// Initialise a new contract and set the code that is to be used by the EVM.
Expand All @@ -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
// 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) {
// 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)
if err != nil {
return ret, err
Expand All @@ -533,11 +549,6 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu
return ret, ErrCodeStoreOutOfGas
}
} 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) {
return ret, ErrCodeStoreOutOfGas
}
Expand Down

0 comments on commit 9be2e01

Please sign in to comment.