Skip to content

Commit b3d1f6d

Browse files
TorstenStueberatheipgherveou0xOmarAgithub-actions[bot]
authored
[Revive] Add configuration to set Ethereum gas scale (#10393)
This PR is based on #10166 (i.e., it merges into the branch `torsten/gas-fixes`). This PR adds a new configuration parameter (`GasScale`) to pallet-revive that allows to change the scale of the Ethereum gas and of the Ethereum gas price. Before this PR, the Ethereum gas price is simply the next fee multiplier of pallet-transaction-payment multiplied by `NativeToEthRatio`. Thus, on Polkadot this is 100_000_000 when the multiplier has its default value of 1. The required gas of a transaction is its total cost divided by the gas price, where the total cost is the sum of the transaction fee and the storage deposit. This leads to a situation where the required gas for a transaction on revive is usually orders of magnitude larger than the required amount of gas on Ethereum. This can lead to issues with tools or systems that interact with revive and hard code expected gas amounts or upper limits of gas amounts. Setting `GasScale` has two effects: - revive's Ethereum gas price is scaled up by the factor `GasScale` - resulting used/estimated gas amounts get scaled down by the factor `GasScale`. ## Technical Details Internally, revive uses exactly the same gas price and gas units as before. Only at the interface these amounts and prices get scaled by `GasScale`. **The actual logical changes are almost trivial and I use `GasScale` at only three places: in `evm_base_fee` to scale the gas price and in the functions `from_ethereum_gas` and `to_ethereum_gas`.** ## Recommended This PR sets `GasScale` for the dev-node to 50_000. This is motivated by the fact that storing a value in a contract storage slot costs `DepositPerChildTrieItem + DepositPerByte * 32`, which is `2_000_000_000 + 10_000_000 * 32` (= `2_320_000_000`) plancks. Before this change the gas price was 1_000_000 wei, so that this equated to 2_320_000_000 gas units. In EVM this operation requires 22_100 gas only. Thus, `GasScale` would need to be about 100_000 in order for `SSTORE` to have similar worst case gas requirements. ## Resolved Issues - fixes paritytech/contract-issues#221 This PR addresses paritytech/contract-issues#18 but we also need to find an appropriate `GasScale` for a mainnet installment of pallet-revive (see [this comment](paritytech/contract-issues#18 (comment))). --------- Co-authored-by: Alexander Theißen <[email protected]> Co-authored-by: PG Herveou <[email protected]> Co-authored-by: Omar Abdulla <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 5b0576c commit b3d1f6d

File tree

8 files changed

+149
-49
lines changed

8 files changed

+149
-49
lines changed

cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,7 @@ impl pallet_revive::Config for Runtime {
12141214
type FeeInfo = pallet_revive::evm::fees::Info<Address, Signature, EthExtraImpl>;
12151215
type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
12161216
type DebugEnabled = ConstBool<false>;
1217+
type GasScale = ConstU32<1000>;
12171218
}
12181219

12191220
parameter_types! {

cumulus/parachains/runtimes/testing/penpal/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ impl pallet_revive::Config for Runtime {
842842
type FeeInfo = pallet_revive::evm::fees::Info<Address, Signature, EthExtraImpl>;
843843
type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
844844
type DebugEnabled = ConstBool<false>;
845+
type GasScale = ConstU32<1000>;
845846
}
846847

847848
impl pallet_sudo::Config for Runtime {

prdoc/pr_10393.prdoc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
title: Add configuration to set Ethereum gas scale
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
This PR adds a new configuration parameter (`GasScale`) to pallet-revive that allows to change the scale of the Ethereum gas and of the Ethereum gas price.
6+
7+
Before this PR, the Ethereum gas price is simply the next fee multiplier of pallet-transaction-payment multiplied by `NativeToEthRatio`. Thus, on Polkadot this is 100_000_000 when the multiplier has its default value of 1.
8+
9+
The required gas of a transaction is its total cost divided by the gas price, where the total cost is the sum of the transaction fee and the storage deposit.
10+
11+
This leads to a situation where the required gas for a transaction on revive is usually orders of magnitude larger than the required amount of gas on Ethereum. This can lead to issues with tools or systems that interact with revive and hard code expected gas amounts or upper limits of gas amounts.
12+
13+
Setting `GasScale` has two effects:
14+
- revive's Ethereum gas price is scaled up by the factor `GasScale`
15+
- resulting used/estimated gas amounts get scaled down by the factor `GasScale`.
16+
17+
## Technical Details
18+
Internally, revive uses exactly the same gas price and gas units as before. Only at the interface these amounts and prices get scaled by `GasScale`.
19+
20+
## Recommended
21+
This PR sets `GasScale` for the dev-node to 50_000.
22+
23+
This is motivated by the fact that storing a value in a contract storage slot costs `DepositPerChildTrieItem + DepositPerByte * 32`, which is `2_000_000_000 + 10_000_000 * 32` (= `2_320_000_000`) plancks. Before this change the gas price was 1_000_000 wei, so that this
24+
equated to 2_320_000_000 gas units. In EVM this operation requires 22_100 gas only.
25+
26+
Thus, `GasScale` would need to be about 100_000 in order for `SSTORE` to have similar worst case gas requirements.
27+
28+
## Resolved Issues
29+
30+
This PR addresses https://github.com/paritytech/contract-issues/issues/18 but we also need to find an appropriate `GasScale` for a mainnet installment of pallet-revive.
31+
crates:
32+
- name: revive-dev-runtime
33+
bump: patch
34+
- name: pallet-revive
35+
bump: major
36+
- name: asset-hub-westend-runtime
37+
bump: patch
38+
- name: penpal-runtime
39+
bump: patch

substrate/bin/node/runtime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,7 @@ impl pallet_revive::Config for Runtime {
15391539
type FeeInfo = pallet_revive::evm::fees::Info<Address, Signature, EthExtraImpl>;
15401540
type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
15411541
type DebugEnabled = ConstBool<false>;
1542+
type GasScale = ConstU32<1000>;
15421543
}
15431544

15441545
impl pallet_sudo::Config for Runtime {

substrate/frame/revive/dev-node/runtime/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub mod genesis_config_presets {
7171
use alloc::{vec, vec::Vec};
7272
use serde_json::Value;
7373

74-
pub const ENDOWMENT: Balance = 1_000_000_001 * DOLLARS;
74+
pub const ENDOWMENT: Balance = 10_000_000_000_001 * DOLLARS;
7575

7676
fn well_known_accounts() -> Vec<AccountId> {
7777
Sr25519Keyring::well_known()
@@ -356,6 +356,7 @@ impl pallet_revive::Config for Runtime {
356356
type Time = Timestamp;
357357
type FeeInfo = FeeInfo<Address, Signature, EthExtraImpl>;
358358
type DebugEnabled = ConstBool<false>;
359+
type GasScale = ConstU32<50000>;
359360
}
360361

361362
pallet_revive::impl_runtime_apis_plus_revive_traits!(

substrate/frame/revive/src/lib.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ pub mod pallet {
347347
/// Allows debug-mode configuration, such as enabling unlimited contract size.
348348
#[pallet::constant]
349349
type DebugEnabled: Get<bool>;
350+
351+
/// This determines the relative scale of our gas price and gas estimates.
352+
///
353+
/// By default, the gas price (in wei) is `FeeInfo::next_fee_multiplier()` multiplied by
354+
/// `NativeToEthRatio`. `GasScale` allows to scale this value: the actual gas price is the
355+
/// default gas price multiplied by `GasScale`.
356+
///
357+
/// As a consequence, gas cost (gas estimates and actual gas usage during transaction) is
358+
/// scaled down by the same factor. Thus, the total transaction cost is not affected by
359+
/// `GasScale` – apart from rounding differences: the transaction cost is always a multiple
360+
/// of the gas price and is derived by rounded up, so that with higher `GasScales` this can
361+
/// lead to higher gas cost as the rounding difference would be larger.
362+
///
363+
/// The main purpose of changing the `GasScale` is to tune the gas cost so that it is closer
364+
/// to standard EVM gas cost and contracts will not run out of gas when tools or code
365+
/// assume hard coded gas limits.
366+
///
367+
/// Requirement: `GasScale` must not be 0
368+
#[pallet::constant]
369+
#[pallet::no_default_bounds]
370+
type GasScale: Get<u32>;
350371
}
351372

352373
/// Container for different types that implement [`DefaultConfig`]` of this pallet.
@@ -375,6 +396,7 @@ pub mod pallet {
375396
pub const DepositPerByte: Balance = deposit(0, 1);
376397
pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
377398
pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
399+
pub const GasScale: u32 = 10u32;
378400
}
379401

380402
/// A type providing default configurations for this pallet in testing environment.
@@ -429,6 +451,7 @@ pub mod pallet {
429451
type FeeInfo = ();
430452
type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
431453
type DebugEnabled = ConstBool<false>;
454+
type GasScale = GasScale;
432455
}
433456
}
434457

@@ -892,6 +915,8 @@ pub mod pallet {
892915
fn integrity_test() {
893916
assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
894917

918+
assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
919+
895920
T::FeeInfo::integrity_test();
896921

897922
// The memory available in the block building runtime
@@ -2114,8 +2139,12 @@ impl<T: Config> Pallet<T> {
21142139

21152140
/// Get the base gas price.
21162141
pub fn evm_base_fee() -> U256 {
2142+
let gas_scale = <T as Config>::GasScale::get();
21172143
let multiplier = T::FeeInfo::next_fee_multiplier();
2118-
multiplier.saturating_mul_int::<u128>(T::NativeToEthRatio::get().into()).into()
2144+
multiplier
2145+
.saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2146+
.saturating_mul(gas_scale.saturated_into())
2147+
.into()
21192148
}
21202149

21212150
/// Build an EVM tracer from the given tracer type.

substrate/frame/revive/src/metering/gas.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
// distributed under the License is distributed on an "AS IS" BASIS,
1414
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1515
// See the License for the specific language governing permissions and
16-
// limitations under the License.
16+
// limitations under the License
1717

1818
use crate::{evm::fees::InfoT, BalanceOf, Config, StorageDeposit};
1919
use frame_support::DebugNoBound;
20+
use sp_core::Get;
2021
use sp_runtime::{FixedPointNumber, Saturating};
2122

2223
/// The type for negative and positive gas amounts.
@@ -52,7 +53,8 @@ impl<T: Config> SignedGas<T> {
5253
/// Transform an Ethereum gas amount coming from outside the metering system and transform into
5354
/// the internally used SignedGas.
5455
pub fn from_ethereum_gas(gas: BalanceOf<T>) -> Self {
55-
Self::Positive(gas)
56+
let gas_scale = <T as Config>::GasScale::get();
57+
Self::Positive(gas.saturating_mul(gas_scale.into()))
5658
}
5759

5860
/// Transform a storage deposit into a gas value. The value will be adjusted by dividing it
@@ -80,8 +82,11 @@ impl<T: Config> SignedGas<T> {
8082
/// Transform the gas amount to an Ethereum gas amount usable for external purposes
8183
/// Returns None if the gas amount is negative.
8284
pub fn to_ethereum_gas(&self) -> Option<BalanceOf<T>> {
85+
let gas_scale: BalanceOf<T> = <T as Config>::GasScale::get().into();
86+
8387
match self {
84-
Positive(amount) => Some(*amount),
88+
Positive(amount) =>
89+
Some((amount.saturating_add(gas_scale.saturating_sub(1u32.into()))) / gas_scale),
8590
Negative(..) => None,
8691
}
8792
}

0 commit comments

Comments
 (0)