Skip to content

Commit 6cfd498

Browse files
gabriele-0201rphmeier
authored andcommitted
fee adjustments account for skipped blocks
1 parent 57815fb commit 6cfd498

File tree

7 files changed

+210
-10
lines changed

7 files changed

+210
-10
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sugondat/chain/pallets/length-fee-adjustment/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", default-
2020
sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
2121
sp-weights = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
2222
sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
23+
cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.4.0", default-features = false, features = ["parameterized-consensus-hook",] }
24+
polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
2325

2426
[dev-dependencies]
2527
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
@@ -37,4 +39,6 @@ std = [
3739
"sp-arithmetic/std",
3840
"pallet-transaction-payment/std",
3941
"pallet-balances/std",
42+
"cumulus-pallet-parachain-system/std",
43+
"polkadot-primitives/std",
4044
]

sugondat/chain/pallets/length-fee-adjustment/src/lib.rs

+104-4
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,27 @@ mod tests;
3838
/// `pallet_transaction_type::Config`.
3939
///
4040
/// `targeted_length_fee_adjustment` is updated at the end of each block inside `on_finalize`
41+
///
42+
/// The pallet also implements `cumulus_pallet_parachain_system::OnSystemEvent` which is used to update `NextLenghtMultiplier`
43+
/// when blocks are skipped, the implementation follows the following formula to update the multiplier:
44+
///
45+
/// ```ignore
46+
/// c_traffic = c_traffic * e^(-target*v*n)
47+
/// ```
48+
///
49+
/// where the exponential is evaluated with the first SkippedBlocksNumberTerms terms, specified in the pallet Config, of the Taylor Series expansion of e^x.
4150
#[frame_support::pallet]
4251
pub mod pallet {
4352

53+
use cumulus_pallet_parachain_system::OnSystemEvent;
4454
use frame_support::pallet_prelude::*;
4555
use frame_system::pallet_prelude::*;
4656
use pallet_transaction_payment::{Multiplier, OnChargeTransaction};
47-
use sp_runtime::{traits::Get, FixedPointNumber, Perquintill, SaturatedConversion, Saturating};
57+
use polkadot_primitives::v6::PersistedValidationData;
58+
use sp_runtime::{
59+
traits::{Get, One, Zero},
60+
FixedPointNumber, Perquintill, SaturatedConversion, Saturating,
61+
};
4862

4963
/// Configure the pallet by specifying the parameters and types on which it depends.
5064
#[pallet::config]
@@ -60,21 +74,39 @@ pub mod pallet {
6074
type MinimumMultiplierBlockSize: Get<Multiplier>;
6175
#[pallet::constant]
6276
type MaximumMultiplierBlockSize: Get<Multiplier>;
77+
78+
/// SkippedBlocksNumberTerms indicates the number of terms used in evaluating e^x in the Taylor expansion.
79+
/// The number of terms ensures that, given the following parameters, the error will be below a certain value.
80+
///
81+
/// t = TargetBlockSize
82+
/// v = AdjustmentVariableBlockSize
83+
/// n = maximum number of skipped blocks
84+
///
85+
/// (1 / (m+1)!) * (n * v * t)^(m + 1) <= err
86+
///
87+
/// Here, m represents the number of terms needed to be below the error.
88+
/// The smallest value of m that satisfies the inequality should be used as the value for this parameter.
89+
///
90+
/// The maximum number of skipped blocks is used to ensure that only the right amount of terms is used,
91+
/// and no terms are wasted. If n is exceeded, the approximation could diverge significantly from the actual value of e^x.
92+
/// This means that producing a block at most every n skipped blocks should be enforced to avoid falling into this error.
93+
#[pallet::constant]
94+
type SkippedBlocksNumberTerms: Get<u32>;
6395
}
6496

6597
#[pallet::pallet]
6698
pub struct Pallet<T>(_);
6799

68-
pub struct NextLengthMultiplierDefualt;
69-
impl Get<Multiplier> for NextLengthMultiplierDefualt {
100+
pub struct NextLengthMultiplierDefault;
101+
impl Get<Multiplier> for NextLengthMultiplierDefault {
70102
fn get() -> Multiplier {
71103
Multiplier::saturating_from_integer(1)
72104
}
73105
}
74106

75107
#[pallet::storage]
76108
pub type NextLengthMultiplier<T: Config> =
77-
StorageValue<_, Multiplier, ValueQuery, NextLengthMultiplierDefualt>;
109+
StorageValue<_, Multiplier, ValueQuery, NextLengthMultiplierDefault>;
78110

79111
pub struct TargetBlockSizeDefault;
80112
impl Get<Perquintill> for TargetBlockSizeDefault {
@@ -87,6 +119,9 @@ pub mod pallet {
87119
pub type TargetBlockSize<T: Config> =
88120
StorageValue<_, Perquintill, ValueQuery, TargetBlockSizeDefault>;
89121

122+
#[pallet::storage]
123+
pub type PrevRelayBlockNumber<T: Config> = StorageValue<_, u32, OptionQuery>;
124+
90125
#[pallet::hooks]
91126
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
92127
fn on_initialize(_: BlockNumberFor<T>) -> Weight {
@@ -178,4 +213,69 @@ pub mod pallet {
178213
multiplier.saturating_mul_int(length_fee)
179214
}
180215
}
216+
217+
impl<T: Config> OnSystemEvent for Pallet<T> {
218+
fn on_validation_data(data: &PersistedValidationData) {
219+
let relay_block_number = data.relay_parent_number;
220+
221+
let prev_relay_block_number = PrevRelayBlockNumber::<T>::get();
222+
PrevRelayBlockNumber::<T>::put(relay_block_number);
223+
224+
// if nothing was already present then this is the first block to
225+
// be executed with the fee adjustment so nothing should be done
226+
let prev_relay_block_number = match prev_relay_block_number {
227+
Some(prev) => prev,
228+
None => return,
229+
};
230+
231+
// It should never be negative because the relay_block_number is surely
232+
// greater than the para_block_number.
233+
// However, saturating it to zero will prevent the multiplier from changing
234+
let relay_parent_distance = relay_block_number.saturating_sub(prev_relay_block_number);
235+
236+
// TODO: update with `n_skipped_blocks = relay_parent_distance.saturating_sub(1)`
237+
// when updating to asynchronous backing
238+
// https://github.com/thrumdev/blobs/issues/166
239+
let n_skipped_blocks = relay_parent_distance.saturating_sub(2) / 2;
240+
241+
let n_skipped_blocks = Multiplier::saturating_from_integer(n_skipped_blocks);
242+
let target_block_size = Multiplier::from(TargetBlockSize::<T>::get());
243+
let adjustment_variable = Multiplier::from(T::AdjustmentVariableBlockSize::get());
244+
245+
let x = adjustment_variable
246+
.saturating_mul(target_block_size)
247+
.saturating_mul(n_skipped_blocks);
248+
249+
// terms = sum_i (x^i / i!) where i in 0..=n_terms
250+
let mut terms = Multiplier::one().saturating_sub(x);
251+
252+
let mut fact = Multiplier::one();
253+
let mut x_i = x;
254+
255+
for index in 2..=T::SkippedBlocksNumberTerms::get() {
256+
x_i = x_i.saturating_mul(x);
257+
258+
if x_i == Multiplier::zero() {
259+
// If x_i is zero, the current term and all subsequent terms become useless,
260+
// and therefore all next terms can be skipped.
261+
// This happens when only a few blocks are skipped, making vnt very small
262+
break;
263+
}
264+
265+
fact = fact.saturating_mul(Multiplier::saturating_from_integer(index));
266+
267+
// (-1)^index
268+
terms = match index & 1 {
269+
1 => terms.saturating_sub(x_i.div(fact)),
270+
_ => terms.saturating_add(x_i.div(fact)),
271+
};
272+
}
273+
274+
NextLengthMultiplier::<T>::mutate(|multiplier| {
275+
*multiplier = multiplier.saturating_mul(terms);
276+
});
277+
}
278+
279+
fn on_validation_code_applied() {}
280+
}
181281
}

sugondat/chain/pallets/length-fee-adjustment/src/mock.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,15 @@ parameter_types! {
7575
pub TransactionByteFee: Balance = 333333u64;
7676
pub MaximumBlockLength: u32 = 5 * 1024 * 1024;
7777
pub AdjustmentVariableBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 840);
78-
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
78+
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 200u128);
7979
pub MaximumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_integer(10);
8080

81+
// TODO: NumberTerms neets to be changed 5
82+
// when updating to asynchronous backing
83+
// https://github.com/thrumdev/blobs/issues/166
84+
// Accepted error is less than 10^(-2)
85+
pub SkippedBlocksNumberTerms: u32 = 3;
86+
8187
pub static WeightToFee: u64 = 1;
8288
pub static OperationalFeeMultiplier: u8 = 5;
8389
}
@@ -106,4 +112,5 @@ impl pallet_sugondat_length_fee_adjustment::Config for Test {
106112
type AdjustmentVariableBlockSize = AdjustmentVariableBlockSize;
107113
type MinimumMultiplierBlockSize = MinimumMultiplierBlockSize;
108114
type MaximumMultiplierBlockSize = MaximumMultiplierBlockSize;
115+
type SkippedBlocksNumberTerms = SkippedBlocksNumberTerms;
109116
}

sugondat/chain/pallets/length-fee-adjustment/src/tests.rs

+72-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ use crate::{
22
mock::{LengthFeeAdjustment, Test},
33
*,
44
};
5+
use cumulus_pallet_parachain_system::OnSystemEvent;
56
use pallet_transaction_payment::Multiplier;
6-
use sp_runtime::BuildStorage;
7-
use sp_runtime::{traits::Get, FixedPointNumber};
7+
use polkadot_primitives::{v6::PersistedValidationData, HeadData};
8+
use sp_runtime::{assert_eq_error_rate, BuildStorage};
9+
use sp_runtime::{
10+
traits::{Get, One},
11+
FixedPointNumber,
12+
};
813
use sp_weights::{Weight, WeightToFee};
914

1015
fn new_test_ext() -> sp_io::TestExternalities {
@@ -46,7 +51,7 @@ fn test_default_next_length_multiplier() {
4651
new_test_ext().execute_with(|| {
4752
assert_eq!(
4853
NextLengthMultiplier::<Test>::get(),
49-
NextLengthMultiplierDefualt::get()
54+
NextLengthMultiplierDefault::get()
5055
)
5156
});
5257
}
@@ -60,3 +65,67 @@ fn test_default_target_block_size() {
6065
)
6166
});
6267
}
68+
69+
#[test]
70+
fn test_skipped_block_multiplier_update() {
71+
// TODO: change max_skipped_blocks to 7200
72+
// when updating to asynchronous backing
73+
// https://github.com/thrumdev/blobs/issues/166
74+
new_test_ext().execute_with(|| {
75+
let max_skipped_blocks = 3600;
76+
for d in (0..max_skipped_blocks).step_by(max_skipped_blocks as usize / 1000) {
77+
// using Multiplier::one() only e^(-vnt) is tested
78+
NextLengthMultiplier::<Test>::put(Multiplier::one());
79+
PrevRelayBlockNumber::<Test>::put(0);
80+
81+
// TODO: update with `1 + d`
82+
// when updating to asynchronous backing
83+
// https://github.com/thrumdev/blobs/issues/166
84+
let relay_data = PersistedValidationData {
85+
parent_head: HeadData(vec![]),
86+
relay_parent_number: 2 + d * 2,
87+
relay_parent_storage_root: sp_core::H256::zero(),
88+
max_pov_size: 0,
89+
};
90+
91+
LengthFeeAdjustment::on_validation_data(&relay_data);
92+
93+
let mul = NextLengthMultiplier::<Test>::get();
94+
95+
// calculate expected result using f64::exp and assert on the error rate
96+
let target = Multiplier::from(TargetBlockSize::<Test>::get()).to_float();
97+
let v = <Test as Config>::AdjustmentVariableBlockSize::get().to_float();
98+
let expected_mul = Multiplier::from_float((-1.0 * target * v * d as f64).exp());
99+
100+
//Accepted error is less than 10^(-2)
101+
assert_eq_error_rate!(mul, expected_mul, Multiplier::from_inner(10000000000000000));
102+
}
103+
});
104+
}
105+
106+
#[test]
107+
fn test_skipped_block_no_prev_data() {
108+
new_test_ext().execute_with(|| {
109+
let relay_parent_number = 156;
110+
let prev_multiplier = Multiplier::from(2);
111+
112+
NextLengthMultiplier::<Test>::put(prev_multiplier);
113+
114+
let relay_data = PersistedValidationData {
115+
parent_head: HeadData(vec![]),
116+
relay_parent_number,
117+
relay_parent_storage_root: sp_core::H256::zero(),
118+
max_pov_size: 0,
119+
};
120+
121+
LengthFeeAdjustment::on_validation_data(&relay_data);
122+
123+
// PrevRelayBlockNumber should be set to what passed in the argument
124+
// as relay_parent_number and NextLengthMultiplier should remain the same
125+
assert_eq!(
126+
PrevRelayBlockNumber::<Test>::get(),
127+
Some(relay_parent_number)
128+
);
129+
assert_eq!(NextLengthMultiplier::<Test>::get(), prev_multiplier);
130+
});
131+
}

sugondat/chain/runtimes/sugondat-kusama/src/lib.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,14 @@ parameter_types! {
293293
// it would require 5298 full blocks to grow back to one.
294294
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 200u128);
295295
pub MaximumMultiplierBlockSize: Multiplier = Bounded::max_value();
296+
297+
// TODO: Change SkippedBlocksNumberTerms to 5
298+
// and keep the expected maximum skipped blocks at half a day,
299+
// which is 7200 blocks when updating to asynchronous backing
300+
// https://github.com/thrumdev/blobs/issues/166
301+
// The accepted error is less than 10^(-2) for an expected
302+
// maximum of 3600 skipped blocks (half a day)
303+
pub SkippedBlocksNumberTerms: u32 = 3;
296304
}
297305

298306
impl pallet_sugondat_length_fee_adjustment::Config for Runtime {
@@ -301,6 +309,7 @@ impl pallet_sugondat_length_fee_adjustment::Config for Runtime {
301309
type AdjustmentVariableBlockSize = AdjustmentVariableBlockSize;
302310
type MaximumMultiplierBlockSize = MaximumMultiplierBlockSize;
303311
type MinimumMultiplierBlockSize = MinimumMultiplierBlockSize;
312+
type SkippedBlocksNumberTerms = SkippedBlocksNumberTerms;
304313
}
305314

306315
pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
@@ -335,7 +344,7 @@ parameter_types! {
335344
impl cumulus_pallet_parachain_system::Config for Runtime {
336345
type WeightInfo = ();
337346
type RuntimeEvent = RuntimeEvent;
338-
type OnSystemEvent = ();
347+
type OnSystemEvent = LengthFeeAdjustment;
339348
type SelfParaId = parachain_info::Pallet<Runtime>;
340349
type OutboundXcmpMessageSource = XcmpQueue;
341350
type DmpQueue = frame_support::traits::EnqueueWithOrigin<MessageQueue, RelayOrigin>;

sugondat/chain/runtimes/test/src/lib.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,14 @@ parameter_types! {
360360
// it would require 5298 full blocks to grow back to one.
361361
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 200u128);
362362
pub MaximumMultiplierBlockSize: Multiplier = Bounded::max_value();
363+
364+
// TODO: Change SkippedBlocksNumberTerms to 5
365+
// and keep the expected maximum skipped blocks at half a day,
366+
// which is 7200 blocks when updating to asynchronous backing
367+
// https://github.com/thrumdev/blobs/issues/166
368+
// The accepted error is less than 10^(-2) for an expected
369+
// maximum of 3600 skipped blocks (half a day)
370+
pub SkippedBlocksNumberTerms: u32 = 3;
363371
}
364372

365373
impl pallet_sugondat_length_fee_adjustment::Config for Runtime {
@@ -368,6 +376,7 @@ impl pallet_sugondat_length_fee_adjustment::Config for Runtime {
368376
type AdjustmentVariableBlockSize = AdjustmentVariableBlockSize;
369377
type MaximumMultiplierBlockSize = MaximumMultiplierBlockSize;
370378
type MinimumMultiplierBlockSize = MinimumMultiplierBlockSize;
379+
type SkippedBlocksNumberTerms = SkippedBlocksNumberTerms;
371380
}
372381

373382
pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
@@ -402,7 +411,7 @@ parameter_types! {
402411
impl cumulus_pallet_parachain_system::Config for Runtime {
403412
type WeightInfo = ();
404413
type RuntimeEvent = RuntimeEvent;
405-
type OnSystemEvent = ();
414+
type OnSystemEvent = LengthFeeAdjustment;
406415
type SelfParaId = parachain_info::Pallet<Runtime>;
407416
type OutboundXcmpMessageSource = XcmpQueue;
408417
type DmpQueue = frame_support::traits::EnqueueWithOrigin<MessageQueue, RelayOrigin>;

0 commit comments

Comments
 (0)