Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify fee modifiers #1420

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 47 additions & 28 deletions runtime/moonbase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use pallet_evm::{
Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot,
FeeCalculator, GasWeightMapping, OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner,
};
use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment};
use pallet_transaction_payment::{CurrencyAdapter, Multiplier, MultiplierUpdate};
pub use parachain_staking::{InflationInfo, Range};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
Expand All @@ -78,14 +78,13 @@ 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,
},
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
},
ApplyExtrinsicResult, FixedPointNumber, Perbill, Percent, Permill, Perquintill,
SaturatedConversion,
ApplyExtrinsicResult, Perbill, Percent, Permill, Perquintill, SaturatedConversion,
};
use sp_std::{
convert::{From, Into, TryFrom},
Expand Down Expand Up @@ -349,7 +348,7 @@ impl pallet_transaction_payment::Config for Runtime {
type OperationalFeeMultiplier = ConstU8<5>;
type WeightToFee = WeightToFee;
type LengthToFee = LengthToFee;
type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Runtime>;
type FeeMultiplierUpdate = FixedGasPrice;
}

impl pallet_sudo::Config for Runtime {
Expand Down Expand Up @@ -385,22 +384,23 @@ 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<Runtime> = 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 {
pub fn gas_price() -> U256 {
(1 * currency::GIGAWEI * currency::SUPPLY_FACTOR).into()
}
pub fn weight_multiplier() -> Multiplier {
// note that 'as_u128' will panic if gas_price() overflows a u128
let weight_price = Self::gas_price() / U256::from(WEIGHT_PER_GAS);
weight_price.as_u128().into()
}
}

impl FeeCalculator for FixedGasPrice {
fn min_gas_price() -> (U256, Weight) {
(
Expand All @@ -410,19 +410,32 @@ impl FeeCalculator for FixedGasPrice {
}
}

/// 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<R> =
TargetedFeeAdjustment<R, TargetBlockFullness, AdjustmentVariable, MinimumMultiplier>;
/// Reminder: Multiplier is a FixedU128, which is a fixed point unsigned in the range
/// [0.000000000000000000, 340282366920938463463.374607431768211455]
impl MultiplierUpdate for FixedGasPrice {
fn min() -> Multiplier {
Self::weight_multiplier()
}
fn target() -> Perquintill {
Perquintill::from_percent(0)
}
fn variability() -> Multiplier {
0.into()
}
}

impl Convert<Multiplier, Multiplier> for FixedGasPrice {
fn convert(_previous: Multiplier) -> Multiplier {
Self::weight_multiplier()
}
}

/// 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<U>
Expand Down Expand Up @@ -1453,4 +1466,10 @@ mod tests {
50
);
}

#[test]
fn fixed_gas_price_as_weight_multiplier_is_known() {
// this also tests that the conversions do not panic (assuming it uses constants)
assert_eq!(FixedGasPrice::weight_multiplier(), 40_000.into());
}
}
163 changes: 94 additions & 69 deletions runtime/moonbase/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ use frame_support::{
fungible::Inspect, fungibles::Inspect as FungiblesInspect, Currency as CurrencyT,
EnsureOrigin, PalletInfo, StorageInfo, StorageInfoTrait,
},
weights::{DispatchClass, Weight},
StorageHasher, Twox128,
};
use moonbase_runtime::{
asset_config::AssetRegistrarMetadata, asset_config::LocalAssetInstance, get,
xcm_config::AssetType, AccountId, AssetId, AssetManager, Assets, Balances, BaseFee,
BlockWeights, Call, CrowdloanRewards, Event, LocalAssets, ParachainStaking, PolkadotXcm,
Precompiles, Runtime, System, XTokens, XcmTransactor, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
xcm_config::AssetType, AccountId, AssetId, AssetManager, Assets, Balances, BaseFee, Call,
CrowdloanRewards, Event, LocalAssets, ParachainStaking, PolkadotXcm, Precompiles, Runtime,
System, XTokens, XcmTransactor, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX,
};

Expand All @@ -49,14 +48,10 @@ use pallet_evm_precompile_assets_erc20::{
};
use xtokens_precompiles::Action as XtokensAction;

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]
Expand Down Expand Up @@ -1900,20 +1895,6 @@ fn xtokens_precompile_transfer_local_asset() {
})
}

fn run_with_system_weight<F>(w: Weight, mut assertions: F)
where
F: FnMut() -> (),
{
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap()
.into();
t.execute_with(|| {
System::set_block_consumed_resources(w, 0);
assertions()
});
}

#[test]
#[rustfmt::skip]
fn length_fee_is_sensible() {
Expand Down Expand Up @@ -1966,52 +1947,6 @@ fn length_fee_is_sensible() {
});
}

#[test]
fn multiplier_can_grow_from_zero() {
let minimum_multiplier = moonbase_runtime::MinimumMultiplier::get();
let target = moonbase_runtime::TargetBlockFullness::get()
* BlockWeights::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::<Runtime>::convert(minimum_multiplier);
assert!(
next > minimum_multiplier,
"{:?} !>= {:?}",
next,
minimum_multiplier
);
})
}

#[test]
#[ignore] // test runs for a very long time
fn multiplier_growth_simulator() {
// 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()
* BlockWeights::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::<Runtime>::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(|| {
Expand Down Expand Up @@ -2570,3 +2505,93 @@ 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;
Comment on lines +2552 to +2554
Copy link
Contributor Author

@notlesh notlesh Jun 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test sucks. Basically, I tried to exploit the fact that remark's weight fn returns its length, but after some debugging this appears to be the encoded length, not the actual remark length.

#[pallet::weight(T::SystemWeightInfo::remark(_remark.len() as u32))]

The overhead increases as the remark grows in size, probably because it takes more bytes to describe the size of a larger vec.

I think the test does its job, but it's pretty ugly. I'd like to find a cleaner way to accomplish the same thing.

assert!(
weight >= known_encoded_overhead_bytes,
"this test only supports weight >= {}",
known_encoded_overhead_bytes
);

let remark = frame_system::Call::<Runtime>::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,
}
);
});
}