From 96059579572790949a94f90bafb819897b794fe4 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 29 Jun 2021 12:56:56 +0200 Subject: [PATCH 1/2] adjust fee calculations --- Cargo.lock | 4 +- runtime/Cargo.toml | 3 ++ runtime/src/constants.rs | 1 - runtime/src/lib.rs | 48 ++++++++++++++++--- runtime/src/tests.rs | 100 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 runtime/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index e9fd8c7b2..da5077802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2320,7 +2320,7 @@ dependencies = [ [[package]] name = "hydra-dx" -version = "5.1.0" +version = "6.0.0" dependencies = [ "frame-benchmarking", "frame-benchmarking-cli", @@ -2443,12 +2443,14 @@ dependencies = [ "parity-scale-codec 2.1.3", "primitives", "serde", + "smallvec 1.6.1", "sp-api", "sp-authority-discovery", "sp-block-builder", "sp-consensus-babe", "sp-core", "sp-inherents", + "sp-io", "sp-npos-elections", "sp-offchain", "sp-runtime", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 18c8d84a9..48fe8daa5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -24,6 +24,7 @@ version = '2.0.0' hex-literal = {optional = true, version = '0.3.1'} serde = {features = ['derive'], optional = true, version = '1.0.101'} tracing-core = {optional = true, version = '0.1.17'} +smallvec = "1.6.1" # local dependencies pallet-asset-registry = {path = '../pallets/asset-registry', default-features = false} @@ -90,6 +91,7 @@ sp-staking = {default-features = false, version = '3.0.0'} sp-std = {default-features = false, version = '3.0.0'} sp-transaction-pool = {default-features = false, version = '3.0.0'} sp-version = {default-features = false, version = '3.0.0'} +sp-io = {default-features = false, version = "3.0.0"} [features] default = ['std'] @@ -120,6 +122,7 @@ std = [ 'orml-tokens/std', 'orml-traits/std', 'pallet-xyk/std', + 'pallet-xyk-rpc-runtime-api/std', 'pallet-claims/std', 'pallet-asset-registry/std', 'pallet-democracy/std', diff --git a/runtime/src/constants.rs b/runtime/src/constants.rs index 385c09445..6c672db33 100644 --- a/runtime/src/constants.rs +++ b/runtime/src/constants.rs @@ -87,7 +87,6 @@ mod tests { // 6s per block assert_eq!(SECS_PER_BLOCK, 6); // 1s = 1000ms - pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); assert_eq!(MILLISECS_PER_BLOCK / 1000, SECS_PER_BLOCK); // Extra check for epoch time because changing it bricks the block production and requires regenesis assert_eq!(EPOCH_DURATION_IN_BLOCKS, 4 * HOURS); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index faeeefc6b..91c6e1908 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -23,6 +23,9 @@ #![allow(clippy::upper_case_acronyms)] #![allow(clippy::from_over_into)] +#[cfg(test)] +mod tests; + // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -59,7 +62,7 @@ pub use frame_support::{ traits::{Filter, KeyOwnerProofSystem, LockIdentifier, Randomness, U128CurrencyToVote}, weights::{ constants::{BlockExecutionWeight, RocksDbWeight, WEIGHT_PER_SECOND}, - DispatchClass, IdentityFee, Pays, Weight, + DispatchClass, Pays, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, StorageValue, }; @@ -170,6 +173,34 @@ const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// We allow for 2 seconds of compute with a 6 second average block time. pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND; +use smallvec::smallvec; +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + + /// Handles converting a weight scalar to a fee value, based on the scale and granularity of the + /// node's balance type. + /// + /// This should typically create a mapping between the following ranges: + /// - [0, MAXIMUM_BLOCK_WEIGHT] + /// - [Balance::min, Balance::max] + /// + /// Yet, it can be used for any other sort of change to weight-fee. Some examples being: + /// - Setting it to `0` will essentially disable the weight fee. + /// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. + fn polynomial() -> WeightToFeeCoefficients { + // extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT + let p = CENTS; // 100_000_000_000 + let q = 10 * Balance::from(ExtrinsicBaseWeight::get()); // 8_079_830_000 + smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, // 12 + }] + } +} + /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { @@ -353,18 +384,23 @@ impl pallet_balances::Config for Runtime { } parameter_types! { - pub const TransactionByteFee: Balance = 1; + pub const TransactionByteFee: Balance = 10 * MILLICENTS; pub const MultiPaymentCurrencySetFee: Pays = Pays::No; - + /// 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); - pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 100_000); + /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to + /// change the fees more rapidly. + 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. pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128); } impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = MultiCurrencyAdapter; type TransactionByteFee = TransactionByteFee; - type WeightToFee = IdentityFee; + type WeightToFee = WeightToFee; type FeeMultiplierUpdate = TargetedFeeAdjustment; } @@ -375,7 +411,7 @@ impl pallet_transaction_multi_payment::Config for Runtime { type AMMPool = XYK; type WeightInfo = pallet_transaction_multi_payment::weights::HydraWeight; type WithdrawFeeForSetCurrency = MultiPaymentCurrencySetFee; - type WeightToFee = IdentityFee; + type WeightToFee = WeightToFee; } impl pallet_genesis_history::Config for Runtime {} diff --git a/runtime/src/tests.rs b/runtime/src/tests.rs new file mode 100644 index 000000000..c565643a8 --- /dev/null +++ b/runtime/src/tests.rs @@ -0,0 +1,100 @@ +//! Tests for the HydraDX Runtime Configuration + +use crate::*; +use codec::Encode; +use frame_support::storage::StorageValue; +use frame_support::weights::{DispatchClass, GetDispatchInfo, WeightToFeePolynomial}; +use sp_runtime::traits::Convert; +use sp_runtime::FixedPointNumber; + +#[test] +fn full_block_cost() { + let max_bytes = *BlockLength::get().max.get(DispatchClass::Normal) as u128; + let length_fee = max_bytes * &TransactionByteFee::get(); + assert_eq!(length_fee, 3_932_160_000_000_000); + + let max_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap_or(1); + let weight_fee = WeightToFee::calc(&max_weight); + assert_eq!(weight_fee, 18_564_747_030_000); + + let target_fee = 395 * DOLLARS + 725_555_013_000; + assert_eq!(ExtrinsicBaseWeight::get() as u128 + length_fee + weight_fee, target_fee); +} + +#[test] +// This function tests that the fee for `ExtrinsicBaseWeight` of weight is correct +fn extrinsic_base_fee_is_correct() { + // `ExtrinsicBaseWeight` should cost 1/10 of a CENT + let base_fee = WeightToFee::calc(&ExtrinsicBaseWeight::get()); + let base_fee_expected = CENTS / 10; + assert!(base_fee.max(base_fee_expected) - base_fee.min(base_fee_expected) < MILLICENTS); +} + +#[test] +#[ignore] +fn transfer_cost() { + let call = >::transfer(Default::default(), Default::default()); + let info = call.get_dispatch_info(); + // convert to outer call + let call = Call::Balances(call); + let len = call.using_encoded(|e| e.len()) as u32; + + let mut ext = sp_io::TestExternalities::new_empty(); + ext.execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::put(Multiplier::saturating_from_integer(1)); + let fee_raw = TransactionPayment::compute_fee_details(len, &info, 0); + let fee = fee_raw.final_fee(); + println!( + "len = {:?} // weight = {:?} // base fee = {:?} // len fee = {:?} // adjusted weight_fee = {:?} // full transfer fee = {:?}\n", + len, + info.weight, + fee_raw.inclusion_fee.clone().unwrap().base_fee, + fee_raw.inclusion_fee.clone().unwrap().len_fee, + fee_raw.inclusion_fee.unwrap().adjusted_weight_fee, + fee, + ); + }); +} + +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] +fn multiplier_can_grow_from_zero() { + let minimum_multiplier = MinimumMultiplier::get(); + let target = 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 = TargetedFeeAdjustment::::convert(minimum_multiplier); + assert!(next > minimum_multiplier, "{:?} !>= {:?}", next, minimum_multiplier); + }) +} + +#[test] +#[ignore] +fn multiplier_growth_simulator() { + // calculate the value of the fee multiplier after one hour of operation with fully loaded blocks + let mut multiplier = Multiplier::saturating_from_integer(1); + let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap(); + for _block_num in 1..=24 * HOURS { + run_with_system_weight(block_weight, || { + let next = TargetedFeeAdjustment::::convert(multiplier); + // ensure that it is growing as well. + assert!(next > multiplier, "{:?} !>= {:?}", next, multiplier); + multiplier = next; + }); + } + println!("multiplier = {:?}", multiplier); +} \ No newline at end of file From d8c4acad8d154c8a328237418277e4b81a603625 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 29 Jun 2021 14:20:12 +0200 Subject: [PATCH 2/2] bump runtime version --- Cargo.lock | 2 +- runtime/Cargo.toml | 2 +- runtime/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da5077802..66d6f9ea6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2394,7 +2394,7 @@ dependencies = [ [[package]] name = "hydra-dx-runtime" -version = "18.0.0" +version = "19.0.0" dependencies = [ "frame-benchmarking", "frame-executive", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 48fe8daa5..c739629fc 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -5,7 +5,7 @@ homepage = 'https://github.com/galacticcouncil/hydradx-node' license = 'Apache 2.0' name = 'hydra-dx-runtime' repository = 'https://github.com/galacticcouncil/hydradx-node' -version = '18.0.0' +version = '19.0.0' [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 91c6e1908..a10e8fd8d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -151,7 +151,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydra-dx"), impl_name: create_runtime_str!("hydra-dx"), authoring_version: 1, - spec_version: 18, + spec_version: 19, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,