partial review feedback

This commit is contained in:
Guillaume Ballet 2024-04-09 16:05:26 +02:00
parent 54bdfb42bd
commit f534a4dc35
9 changed files with 59 additions and 48 deletions

View file

@ -194,7 +194,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb, big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
}
for i := 0; txIt.Next(); i++ {

View file

@ -361,8 +361,10 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
// Add the balance of each withdrawal to the witness, no gas will
// be charged.
if chain.Config().IsEIP4762(header.Number, header.Time) {
state.Witness().TouchFullAccount(w.Address[:], true)
state.Witness().TouchBalance(w.Address[:], true)
}
}
// No block reward which is issued by consensus layer instead.

View file

@ -89,6 +89,8 @@ func (aw *AccessWitness) Copy() *AccessWitness {
return naw
}
// TouchFullAccount returns the gas to be charged for each of the currently cold
// member fields of an account.
func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 {
var gas uint64
for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
@ -97,6 +99,9 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 {
return gas
}
// TouchAndChargeMessageCall returns the gas to be charged for each of the currently
// cold member fields of an account, that need to be touched when making a message
// call to that account.
func (aw *AccessWitness) TouchAndChargeMessageCall(destination []byte) uint64 {
var gas uint64
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
@ -104,6 +109,8 @@ func (aw *AccessWitness) TouchAndChargeMessageCall(destination []byte) uint64 {
return gas
}
// TouchAndChargeValueTransfer returns the gas to be charged for each of the currently
// cold balance member fields of the caller and the callee accounts.
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 {
var gas uint64
gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
@ -111,8 +118,8 @@ func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []by
return gas
}
// TouchAndChargeContractCreateInit charges access costs to initiate
// a contract creation
// TouchAndChargeContractCreateInit returns the access gas costs for the initialization of
// a contract creation.
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
var gas uint64
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
@ -123,41 +130,30 @@ func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSen
return gas
}
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
// TouchTxOrigin adds the member fields of the sender account to the witness,
// so that cold accesses are not charged, since they are covered by the 21000 gas.
func (aw *AccessWitness) TouchTxOrigin(originAddr []byte) {
for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BalanceLeafKey || i == utils.NonceLeafKey)
}
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
// so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling.
// This is the reason why we return 0 instead of `gas`.
// Note that we still have to touch the addresses to make sure the witness is correct.
return 0
}
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey, false)
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey, false)
if sendsValue {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
} else {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, false)
// TouchTxDestination adds the member fields of the sender account to the witness,
// so that cold accesses are not charged, since they are covered by the 21000 gas.
func (aw *AccessWitness) TouchTxDestination(targetAddr []byte, sendsValue bool) {
for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, byte(i), i == utils.VersionLeafKey && sendsValue)
}
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
// so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling.
// This is the reason why we return 0 instead of `gas`.
// Note that we still have to touch the addresses to make sure the witness is correct.
return 0
}
// TouchSlotAndChargeGas returns the amount of gas to be charged for a cold storage access.
func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool) uint64 {
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
}
// touchAddressAndChargeGas adds any missing access event to the witness, and returns the cold
// access cost to be charged, if need be.
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, treeIndex, subIndex, isWrite)

View file

@ -423,17 +423,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
targetAddr := msg.To
originAddr := msg.From
statelessGasOrigin := st.evm.Accesses.TouchTxOriginAndComputeGas(originAddr.Bytes())
if !tryConsumeGas(&st.gasRemaining, statelessGasOrigin) {
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
}
st.evm.Accesses.TouchTxOrigin(originAddr.Bytes())
originNonce := st.evm.StateDB.GetNonce(originAddr)
if msg.To != nil {
statelessGasDest := st.evm.Accesses.TouchTxExistingAndComputeGas(targetAddr.Bytes(), msg.Value.Sign() != 0)
if !tryConsumeGas(&st.gasRemaining, statelessGasDest) {
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
}
st.evm.Accesses.TouchTxDestination(targetAddr.Bytes(), msg.Value.Sign() != 0)
// ensure the code size ends up in the access witness
st.evm.StateDB.GetCodeSize(*targetAddr)
@ -501,7 +495,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// add the coinbase to the witness iff the fee is greater than 0
if rules.IsEIP4762 && fee.Sign() != 0 {
st.evm.Accesses.TouchFullAccount(st.evm.Context.Coinbase[:], true)
st.evm.Accesses.TouchBalance(st.evm.Context.Coinbase[:], true)
}
}

View file

@ -272,6 +272,12 @@ const (
GasChangeCallStorageColdAccess GasChangeReason = 13
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
GasChangeCallFailedExecution GasChangeReason = 14
// GasChangeWitnessContractInit is the amount charged for adding to the witness during the contract creation initialization step
GasChangeWitnessContractInit GasChangeReason = 15
// GasChangeWitnessContractCreation is the amount charged for adding to the witness during the contract creation finalization step
GasChangeWitnessContractCreation GasChangeReason = 16
// GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks
GasChangeWitnessCodeChunk GasChangeReason = 17
// 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.

View file

@ -334,6 +334,7 @@ func enable4762(jt *JumpTable) {
jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762
jt[EXTCODECOPY].constantGas = 0
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762
jt[CODECOPY].dynamicGas = gasCodeCopyEip4762
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762
jt[CREATE].constantGas = params.CreateNGasEip4762
jt[CREATE2].constantGas = params.CreateNGasEip4762

View file

@ -510,7 +510,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeUnspecified) {
if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
err = ErrOutOfGas
}
}
@ -541,11 +541,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
} else {
// Contract creation completed, touch the missing fields in the contract
if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true), evm.Config.Tracer, tracing.GasChangeUnspecified) {
if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
err = ErrOutOfGas
}
if err != nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeUnspecified) {
if err != nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
err = ErrOutOfGas
}
}

View file

@ -364,16 +364,8 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
uint64CodeOffset = math.MaxUint64
}
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
if interpreter.evm.chainRules.IsEIP4762 && !scope.Contract.IsDeployment {
statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
}
}
scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy)
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
return nil, nil
}

View file

@ -108,6 +108,26 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
return statelessGas, nil
}
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return 0, err
}
var (
codeOffset = stack.Back(1)
length = stack.Back(2)
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = math.MaxUint64
}
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
if !contract.IsDeployment {
gas += evm.Accesses.TouchCodeChunksRangeAndChargeGas(contract.Address().Bytes(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
}
return gas, nil
}
func gasExtCodeCopyEIP4762(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)