diff --git a/runtime/moonbase/src/lib.rs b/runtime/moonbase/src/lib.rs index a5220e4e9d3..dee6e1b33bd 100644 --- a/runtime/moonbase/src/lib.rs +++ b/runtime/moonbase/src/lib.rs @@ -48,7 +48,7 @@ use frame_support::{ }, weights::{ constants::{RocksDbWeight, WEIGHT_PER_SECOND}, - ConstantMultiplier, DispatchClass, GetDispatchInfo, Weight, WeightToFeeCoefficient, + DispatchClass, GetDispatchInfo, IdentityFee, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, @@ -70,8 +70,8 @@ use pallet_evm::{ Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, GasWeightMapping, OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner, }; +use pallet_transaction_payment::{CurrencyAdapter, Multiplier, MultiplierUpdate}; pub use pallet_parachain_staking::{InflationInfo, Range}; -use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; @@ -79,7 +79,7 @@ use sp_core::{OpaqueMetadata, H160, H256, U256}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentityLookup, + BlakeTwo256, Block as BlockT, Convert, DispatchInfoOf, Dispatchable, IdentityLookup, PostDispatchInfoOf, UniqueSaturatedInto, }, transaction_validity::{ @@ -351,9 +351,9 @@ impl pallet_transaction_payment::Config for Runtime { type Event = Event; type OnChargeTransaction = CurrencyAdapter>; type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = ConstantMultiplier>; + type WeightToFee = IdentityFee; type LengthToFee = LengthToFee; - type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type FeeMultiplierUpdate = FixedGasPrice; } impl pallet_sudo::Config for Runtime { @@ -389,44 +389,56 @@ impl pallet_evm::GasWeightMapping for MoonbeamGasWeightMapping { parameter_types! { pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT / WEIGHT_PER_GAS); - /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less - /// than this will decrease the weight and more will increase. - pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); - /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to - /// change the fees more rapidly. This low value causes changes to occur slowly over time. - pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); - /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure - /// that combined with `AdjustmentVariable`, we can recover from the minimum. - /// See `multiplier_can_grow_from_zero` in integration_tests.rs. - /// This value is currently only used by pallet-transaction-payment as an assertion that the - /// next multiplier is always > min value. - pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); pub PrecompilesValue: MoonbasePrecompiles = MoonbasePrecompiles::<_>::new(); } +/// Implementation of both Ethereum and Substrate congestion-based fee modifiers (EIP-1559 and +/// pallet-transaction-payment) which returns a fixed fee. pub struct FixedGasPrice; +impl FixedGasPrice { + fn gas_price() -> U256 { + (1 * currency::GIGAWEI * currency::SUPPLY_FACTOR).into() + } +} + impl FeeCalculator for FixedGasPrice { fn min_gas_price() -> (U256, Weight) { - ( - (1 * currency::GIGAWEI * currency::SUPPLY_FACTOR).into(), - 0u64, - ) + (Self::gas_price(), 0u64) } } -/// Parameterized slow adjusting fee updated based on -/// https://w3f-research.readthedocs.io/en/latest/polkadot/overview/2-token-economics.html#-2.-slow-adjusting-mechanism // editorconfig-checker-disable-line +/// Implementation of MultiplierUpdate which uses FixedGasPrice to update +/// pallet-transaction-payment's fee modifier. /// -/// The adjustment algorithm boils down to: +/// Note that the MultiplierUpdate trait itself is currently only used in transaction-payment's +/// integrity_test() hook. The important conversion occurs in its on_finalize() hook and uses the +/// Convert trait. /// -/// diff = (previous_block_weight - target) / maximum_block_weight -/// next_multiplier = prev_multiplier * (1 + (v * diff) + ((v * diff)^2 / 2)) -/// assert(next_multiplier > min) -/// where: v is AdjustmentVariable -/// target is TargetBlockFullness -/// min is MinimumMultiplier -pub type SlowAdjustingFeeUpdate = - TargetedFeeAdjustment; +/// Reminder: FixedU128 is a fixed point unsigned in the range +/// [0.000000000000000000, 340282366920938463463.374607431768211455] +impl MultiplierUpdate for FixedGasPrice { + fn min() -> Multiplier { + Self::gas_price() + .saturating_mul(WEIGHT_PER_GAS.into()) + .as_u128() // TODO: this panics. we should enforce the upper bound from FixedU128 + .into() + } + fn target() -> Perquintill { + Perquintill::from_percent(0) + } + fn variability() -> Multiplier { + 0.into() + } +} + +impl Convert for FixedGasPrice { + fn convert(_previous: Multiplier) -> Multiplier { + Self::gas_price() + .saturating_mul(WEIGHT_PER_GAS.into()) + .as_u128() // TODO: this panics. we should enforce the upper bound from FixedU128 + .into() + } +} /// The author inherent provides an AccountId, but pallet evm needs an H160. /// This simple adapter makes the conversion for any types T, U such that T: Into diff --git a/runtime/moonbase/tests/integration_test.rs b/runtime/moonbase/tests/integration_test.rs index ea36bc1ae79..47d128b1151 100644 --- a/runtime/moonbase/tests/integration_test.rs +++ b/runtime/moonbase/tests/integration_test.rs @@ -29,8 +29,8 @@ use frame_support::{ fungible::Inspect, fungibles::Inspect as FungiblesInspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo, StorageInfoTrait, }, - weights::{DispatchClass, Weight}, StorageHasher, Twox128, + weights::DispatchClass, }; use moonbase_runtime::{ asset_config::AssetRegistrarMetadata, asset_config::LocalAssetInstance, get, @@ -60,10 +60,7 @@ use pallet_transaction_payment::Multiplier; use parity_scale_codec::Encode; use sha3::{Digest, Keccak256}; use sp_core::{crypto::UncheckedFrom, ByteArray, Pair, H160, U256}; -use sp_runtime::{ - traits::{Convert, One}, - DispatchError, ModuleError, TokenError, -}; +use sp_runtime::{DispatchError, ModuleError, TokenError}; use xcm::latest::prelude::*; #[test] @@ -1948,20 +1945,6 @@ fn xtokens_precompile_transfer_local_asset() { }) } -fn run_with_system_weight(w: Weight, mut assertions: F) -where - F: FnMut() -> (), -{ - let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap() - .into(); - t.execute_with(|| { - System::set_block_consumed_resources(w, 0); - assertions() - }); -} - #[test] #[rustfmt::skip] fn length_fee_is_sensible() { @@ -1997,56 +1980,6 @@ fn length_fee_is_sensible() { }); } -#[test] -fn multiplier_can_grow_from_zero() { - use frame_support::traits::Get; - - let minimum_multiplier = moonbase_runtime::MinimumMultiplier::get(); - let target = moonbase_runtime::TargetBlockFullness::get() - * RuntimeBlockWeights::get() - .get(DispatchClass::Normal) - .max_total - .unwrap(); - // if the min is too small, then this will not change, and we are doomed forever. - // the weight is 1/100th bigger than target. - run_with_system_weight(target * 101 / 100, || { - let next = moonbase_runtime::SlowAdjustingFeeUpdate::::convert(minimum_multiplier); - assert!( - next > minimum_multiplier, - "{:?} !>= {:?}", - next, - minimum_multiplier - ); - }) -} - -#[test] -#[ignore] // test runs for a very long time -fn multiplier_growth_simulator() { - use frame_support::traits::Get; - - // assume the multiplier is initially set to its minimum. We update it with values twice the - //target (target is 25%, thus 50%) and we see at which point it reaches 1. - let mut multiplier = moonbase_runtime::MinimumMultiplier::get(); - let block_weight = moonbase_runtime::TargetBlockFullness::get() - * RuntimeBlockWeights::get() - .get(DispatchClass::Normal) - .max_total - .unwrap() - * 2; - let mut blocks = 0; - while multiplier <= Multiplier::one() { - run_with_system_weight(block_weight, || { - let next = moonbase_runtime::SlowAdjustingFeeUpdate::::convert(multiplier); - // ensure that it is growing as well. - assert!(next > multiplier, "{:?} !>= {:?}", next, multiplier); - multiplier = next; - }); - blocks += 1; - println!("block = {} multiplier {:?}", blocks, multiplier); - } -} - #[test] fn ethereum_invalid_transaction() { ExtBuilder::default().build().execute_with(|| { @@ -2606,6 +2539,96 @@ fn base_fee_should_default_to_associate_type_value() { }); } +#[test] +fn substrate_based_weight_fees_are_known() { + use pallet_transaction_payment::{FeeDetails, InclusionFee}; + use sp_runtime::testing::TestXt; + + let generate_xt = |weight: u128| { + // an empty remark has an encoded size of 12 in this case. we want to generate a txn with + // a specific weight and remark's weight is its encoded size, so we simply adjust for this + // (and don't allow a size less than 12). + // + // TODO: there may be a better way to do this (use a different pub fn from + // transaction-payment for testing?) + let known_encoded_overhead_bytes = 11; + assert!( + weight >= known_encoded_overhead_bytes, + "this test only supports weight >= {}", + known_encoded_overhead_bytes + ); + + let remark = frame_system::Call::::remark { + remark: vec![1; (weight - known_encoded_overhead_bytes) as usize], + }; + let signed_xt = TestXt::<_, ()>::new(remark, Some((0, ()))); + signed_xt + }; + + ExtBuilder::default().build().execute_with(|| { + use frame_support::weights::DispatchClass; + use moonbase_runtime::currency; + + let query_fees = |weight: u128, len: u32| { + let signed_xt = generate_xt(weight); + + // TODO: this returned weight doesn't seem to follow the weight fn declared for + // remark... + // + // NOTE: another alternative would be to call pallet_transaction_payment's + // compute_actual_fee_details() which gives better control over inputs + + moonbase_runtime::TransactionPayment::query_fee_details(signed_xt, len) + }; + + let base_extrinsic = moonbase_runtime::BlockWeights::get() + .per_class + .get(DispatchClass::Normal) + .base_extrinsic; + let expected_base_fee = base_extrinsic as u128 * currency::WEIGHT_FEE; + assert_eq!(6250000000000, expected_base_fee); + + let fee_details = query_fees(11, 0); + assert_eq!( + fee_details, + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: expected_base_fee, + len_fee: 0, + adjusted_weight_fee: 11 * currency::WEIGHT_FEE, + }), + tip: 0, + } + ); + + let fee_details = query_fees(12, 0); + assert_eq!( + fee_details, + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: expected_base_fee, + len_fee: 0, + adjusted_weight_fee: 12 * currency::WEIGHT_FEE, + }), + tip: 0, + } + ); + + let fee_details = query_fees(64, 0); + assert_eq!( + fee_details, + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: expected_base_fee, + len_fee: 0, + adjusted_weight_fee: 64 * currency::WEIGHT_FEE, + }), + tip: 0, + } + ); + }); +} + #[test] fn substrate_based_fees_zero_txn_costs_only_base_extrinsic() { use frame_support::weights::{DispatchInfo, Pays};