From de96e49a143de68210582bf31d77aa158ddb7bd6 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Mon, 25 Sep 2023 16:57:17 +0200 Subject: [PATCH] Documentation & benchmarking code --- Cargo.lock | 1 + pallets/dynamic-evm-base-fee/Cargo.toml | 8 ++ .../dynamic-evm-base-fee/src/benchmarking.rs | 75 ++++++++++++++++ pallets/dynamic-evm-base-fee/src/lib.rs | 85 +++++++++++++++---- 4 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 pallets/dynamic-evm-base-fee/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index 296de3f4b1..48840c4151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7151,6 +7151,7 @@ name = "pallet-dynamic-evm-base-fee" version = "0.1.0" dependencies = [ "fp-evm", + "frame-benchmarking", "frame-support", "frame-system", "num-traits", diff --git a/pallets/dynamic-evm-base-fee/Cargo.toml b/pallets/dynamic-evm-base-fee/Cargo.toml index 7106dd350f..e9c2177b6d 100644 --- a/pallets/dynamic-evm-base-fee/Cargo.toml +++ b/pallets/dynamic-evm-base-fee/Cargo.toml @@ -13,6 +13,7 @@ parity-scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -41,9 +42,16 @@ std = [ "pallet-transaction-payment/std", "pallet-balances/std", "pallet-timestamp/std", + "frame-benchmarking/std", # Frontier "fp-evm/std", ] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs new file mode 100644 index 0000000000..4b3da9bc05 --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -0,0 +1,75 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +use super::*; + +use frame_benchmarking::v2::*; +use frame_support::traits::Hooks; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn base_fee_per_gas_adjustment() { + let (first_block, second_block) = (T::BlockNumber::from(1u32), T::BlockNumber::from(2u32)); + let init_bfpg = BaseFeePerGas::::get(); + + // Setup actions, should ensure default value is written to storage. + Pallet::::on_initialize(first_block); + Pallet::::on_finalize(first_block); + Pallet::::on_initialize(second_block); + + #[block] + { + Pallet::::on_finalize(second_block); + } + + // Ensure that the value has changed. + assert!(BaseFeePerGas::::get() != init_bfpg); + } + + #[benchmark] + fn set_base_fee_per_gas() { + let old_bfpg = BaseFeePerGas::::get(); + let new_bfpg = old_bfpg + 1; + + #[extrinsic_call] + _(RawOrigin::Root, new_bfpg); + + // Ensure that the value has changed. + assert_eq!(BaseFeePerGas::::get(), new_bfpg); + } + + impl_benchmark_test_suite!( + Pallet, + crate::benchmarking::tests::new_test_ext(), + crate::mock::TestRuntime, + ); +} + +#[cfg(test)] +mod tests { + use crate::mock; + use sp_io::TestExternalities; + + pub fn new_test_ext() -> TestExternalities { + mock::ExtBuilder::default().build() + } +} diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 494ce047e1..c5fd9d170d 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -16,19 +16,59 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . -//! TODO: Rustdoc!!! - -// TODO: remove this comment later -// Max amount that adjustment factor will be able to change on live networks using the new tokenomics will be: -// -// c_n = c_n-1 * (1 + adjustment + adjustment^2/2) -// -// adjustment = v * (s - s*) -// -// Biggest possible adjustment between 2 blocks is: 0.000015 * (1 - 0.25) = 0.000_011_25 -// Expressed as ratio: 11_250 / 1_000_000_000. -// This is a much smaller change compared to the max step limit ratio we'll use to limit bfpg adaptation. -// This means that once equilibrium is reached, the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating it's effect. +//! Dynamic Evm Base Fee Pallet +//! +//! ## Overview +//! +//! The pallet is responsible for calculating `Base Fee Per Gas` value, according to the current system parameters. +//! This is not like `EIP-1559`, instead it's intended for `Astar` and `Astar-like` networks, which allow both +//! **Substrate native transactions** (which in `Astar` case reuse Polkadot transaction fee approach) +//! and **EVM transactions** (which use `Base Fee Per Gas`). +//! +//! For a more detailed description, reader is advised to refer to Astar Network forum post about [Tokenomics 2.0](https://forum.astar.network/t/astar-tokenomics-2-0-a-dynamically-adjusted-inflation/4924). +//! +//! ## Approach +//! +//! The core formula this pallet tries to satisfy is: +//! +//! base_fee_per_gas = adjustment_factor * weight_factor * 25 / 98974 +//! +//! Where: +//! * **adjustment_factor** - is a value that changes in-between the blocks, related to the block fill ratio. +//! * **weight_factor** - fixed constant, used to convert consumed _weight_ to _fee_. +//! +//! The implementation doesn't make any hard requirements on these values, and only requires that a type implementing `Get<_>` provides them. +//! +//! ## Implementation +//! +//! The core logic is implemented in `on_finalize` hook, which is called at the end of each block. +//! This pallet's hook should be called AFTER whicever pallet's hook is responsible for updating **adjustment factor**. +//! +//! The hook will calculate the ideal new `base_fee_per_gas` value, and then clamp it in between the allowed limits. +//! +//! ## Interface +//! +//! Pallet provides an implementation of `FeeCalculator` trait. This makes it usable directly in `pallet-evm`. +//! +//! A _root-only_ extrinsic is provided to allow setting the `base_fee_per_gas` value manually. +//! +//! ## Practical Remarks +//! +//! According to the proposed **Tokenomics 2.0**, max amount that adjustment factor will be able to change on live networks in-between blocks is: +//! +//! adjustment_new = adjustment_old * (1 + adj + adj^2/2) +//! +//! adj = v * (s - s*) +//! --> recommended _v_ value: 0.000_015 +//! --> larges 's' delta: (1 - 0.25) = **0.75** +//! +//! adj = 0.000015 * (1 - 0.25) = **0.000_011_25** +//! (1 + 0.000_011_25 + 0.000_011_25^2/2) = (1 + 0.000_011_25 + 0.000_000_000_063_281) = **1,000_011_250_063_281** +//! +//! Discarding the **1**, and only considering the decimals, this can be expressed as ratio: +//! Expressed as ratio: 11_250_063_281 / 1_000_000_000_000_000. +//! This is a much smaller change compared to the max step limit ratio we'll use to limit bfpg alignment. +//! This means that once equilibrium is reached (fees are aligned), the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating it's effect. #![cfg_attr(not(feature = "std"), no_std)] @@ -43,6 +83,15 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +// TODO: remove this after proper benchmarking! +pub trait WeightInfo { + fn base_fee_per_gas_adjustment() -> Weight; + fn set_base_fee_per_gas() -> Weight; +} + #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; @@ -71,6 +120,8 @@ pub mod pallet { /// It's expressed as percentage, and used to calculate the delta between the old and new value. /// E.g. if the current 'base fee per gas' is 100, and the limit is 10%, then the new base fee per gas can be between 90 and 110. type StepLimitRatio: Get; + /// Weight information for extrinsics & functions of this pallet. + type WeightInfo: WeightInfo; } #[pallet::type_value] @@ -97,9 +148,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: T::BlockNumber) -> Weight { - // TODO: benchmark this! - let db_weight = ::DbWeight::get(); - db_weight.reads_writes(2, 1) + T::WeightInfo::base_fee_per_gas_adjustment() } fn on_finalize(_n: ::BlockNumber) { @@ -160,8 +209,10 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// `root-only` extrinsic to set the `base_fee_per_gas` value manually. + /// The specified value has to respect min & max limits configured in the runtime. #[pallet::call_index(0)] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO: weight! + #[pallet::weight(T::WeightInfo::set_base_fee_per_gas())] pub fn set_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { ensure_root(origin)?; ensure!(