Skip to content
Merged
94 changes: 62 additions & 32 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (*multigas.MultiGas, uint64,
fee := newTotalFee - mem.lastGasCost
mem.lastGasCost = newTotalFee

return multigas.ZeroGas(), fee, nil
// Memory expansion considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
return multigas.ComputationGas(fee), fee, nil
}
return multigas.ZeroGas(), 0, nil
}
Expand Down Expand Up @@ -404,82 +406,106 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor

func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
var (
gas uint64
multiGas = multigas.ZeroGas()
transfersValue = !stack.Back(2).IsZero()
address = common.Address(stack.Back(1).Bytes20())
)

// Storage slot writes (zero → nonzero) considered as storage growth.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) {
gas += params.CallNewAccountGas
multiGas.SafeIncrement(multigas.ResourceKindStorageGrowth, params.CallNewAccountGas)
}
} else if !evm.StateDB.Exist(address) {
gas += params.CallNewAccountGas
multiGas.SafeIncrement(multigas.ResourceKindStorageGrowth, params.CallNewAccountGas)
}

// Value transfer to non-empty account considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if transfersValue && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
multiGas.SafeIncrement(multigas.ResourceKindComputation, params.CallValueTransferGas)
}
multiGas, memoryGas, err := memoryGasCost(mem, memorySize)

memoryMultiGas, _, err := memoryGasCost(mem, memorySize)
if err != nil {
return multigas.ZeroGas(), 0, err
}
// TODO(NIT-3484): Update multi dimensional gas here
var overflow bool
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
multiGas, overflow := multiGas.SafeAdd(multiGas, memoryMultiGas)
if overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}

if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
if overflow {
valueTransferGas := evm.AccessEvents.ValueTransferGas(contract.Address(), address)
// Account lookups considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow := multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, valueTransferGas); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
}
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))

singleGas, _ := multiGas.SingleGas()
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, singleGas, stack.Back(0))
if err != nil {
return multigas.ZeroGas(), 0, err
}
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
// Call gas forwarding considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow = multiGas.SafeIncrement(multigas.ResourceKindComputation, evm.callGasTemp); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}

return multiGas, gas, nil
singleGas, _ = multiGas.SingleGas()
return multiGas, singleGas, nil
}

func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
multiGas, memoryGas, err := memoryGasCost(mem, memorySize)
memoryMultiGas, _, err := memoryGasCost(mem, memorySize)
if err != nil {
return multigas.ZeroGas(), 0, err
}
var (
gas uint64
multiGas = multigas.ZeroGas()
overflow bool
)
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
// Value transfer to non-empty account considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindComputation, params.CallValueTransferGas)
}
// TODO(NIT-3484): Update multi dimensional gas here
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
multiGas, overflow = multiGas.SafeAdd(multiGas, memoryMultiGas)
if overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
address := common.Address(stack.Back(1).Bytes20())
transfersValue := !stack.Back(2).IsZero()
if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
if overflow {
valueTransferGas := evm.AccessEvents.ValueTransferGas(contract.Address(), address)
// Account lookups considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow = multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, valueTransferGas); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
}
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))

singleGas, _ := multiGas.SingleGas()
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, singleGas, stack.Back(0))
if err != nil {
return multigas.ZeroGas(), 0, err
}
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
// Call gas forwarding considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow = multiGas.SafeIncrement(multigas.ResourceKindComputation, evm.callGasTemp); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
return multiGas, gas, nil

singleGas, _ = multiGas.SingleGas()
return multiGas, singleGas, nil
}

func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
Expand All @@ -491,12 +517,14 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if err != nil {
return multigas.ZeroGas(), 0, err
}
var overflow bool
// TODO(NIT-3484): Update multi dimensional gas here
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
// Call gas forwarding considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow := multiGas.SafeIncrement(multigas.ResourceKindComputation, evm.callGasTemp); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
return multiGas, gas, nil

singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil
}

func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
Expand All @@ -508,12 +536,14 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
if err != nil {
return multigas.ZeroGas(), 0, err
}
var overflow bool
// TODO(NIT-3484): Update multi dimensional gas here
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
// Call gas forwarding considered as computation.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow := multiGas.SafeIncrement(multigas.ResourceKindComputation, evm.callGasTemp); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
return multiGas, gas, nil

singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil
}

func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
Expand Down
45 changes: 34 additions & 11 deletions core/vm/operations_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
// Cold slot access considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, params.ColdSloadCostEIP2929)

// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
Expand All @@ -51,13 +54,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil

// Warm slot access considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, params.WarmStorageReadCostEIP2929)
singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil // SLOAD_GAS
}
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
// Creating a new slot considered as storage growth.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageGrowth, params.SstoreSetGasEIP2200)
singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil
Expand All @@ -67,6 +75,9 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)

// Storage slot writes (nonzero → zero) considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, params.SstoreResetGasEIP2200-params.ColdSloadCostEIP2929)
singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil // write existing slot (2.1.2)
Expand Down Expand Up @@ -94,6 +105,9 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)

// Warm slot access considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, params.WarmStorageReadCostEIP2929)
singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil // dirty update (2.2)
Expand Down Expand Up @@ -194,12 +208,14 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
// also become correctly reported to tracers.
contract.Gas += coldCost

// TODO(NIT-3484): Update multi dimensional gas here
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
// Cold slot access considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
if overflow := multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, coldCost); overflow {
return multigas.ZeroGas(), 0, ErrGasUintOverflow
}
return multigas.ZeroGas(), gas, nil

singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil
}
}

Expand Down Expand Up @@ -265,8 +281,8 @@ var (
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (*multigas.MultiGas, uint64, error) {
var (
total uint64 // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20())
multiGas = multigas.ZeroGas() // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20())
)

// Check slot presence in the access list
Expand All @@ -280,7 +296,9 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return multigas.ZeroGas(), 0, ErrOutOfGas
}
total += coldCost
// Cold slot access considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, coldCost)
}

// Check if code is a delegation and if so, charge for resolution.
Expand All @@ -295,7 +313,10 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return multigas.ZeroGas(), 0, ErrOutOfGas
}
total += cost

// Target address resolution considered as storage access.
// See rationale in: https://github.com/OffchainLabs/nitro/blob/master/docs/decisions/0002-multi-dimensional-gas-metering.md
multiGas.SafeIncrement(multigas.ResourceKindStorageAccess, cost)
}

// Now call the old calculator, which takes into account
Expand All @@ -312,12 +333,14 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
// 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
contract.Gas += multiGas.Get(multigas.ResourceKindStorageAccess)

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

singleGas, _ := multiGas.SingleGas()
return multiGas, singleGas, nil
}
}
Loading
Loading