Skip to content

Commit 1e8176e

Browse files
gabriele-0201rphmeier
authored andcommitted
init blobs fees adjustments
1 parent 7d87615 commit 1e8176e

File tree

6 files changed

+331
-12
lines changed

6 files changed

+331
-12
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sugondat-chain/primitives/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use sp_runtime::{
55
MultiSignature,
66
};
77

8+
// TODO: probably this could be moved into runtimes/kusama-runtime/src/constants.rs
9+
pub const MAXIMUM_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;
10+
811
/// An index to a block.
912
pub type BlockNumber = u32;
1013

sugondat-chain/runtimes/sugondat-kusama/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ sp-session = { git = "https://github.com/paritytech/polkadot-sdk", default-featu
5353
sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
5454
sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
5555
sp-version = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
56+
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
57+
sp-weights = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
5658

5759
# Polkadot
5860
pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
@@ -78,7 +80,6 @@ parachain-info = { package = "staging-parachain-info", git = "https://github.com
7880
parachains-common = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.4.0", default-features = false }
7981

8082
[dev-dependencies]
81-
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
8283
sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
8384

8485
[features]
@@ -132,6 +133,7 @@ std = [
132133
"sp-transaction-pool/std",
133134
"sp-version/std",
134135
"sp-io/std",
136+
"sp-weights/std",
135137
"substrate-wasm-builder",
136138
"xcm-builder/std",
137139
"xcm-executor/std",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub mod consensus {
2525
// Time is measured by number of blocks.
2626
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
2727
pub const HOURS: BlockNumber = MINUTES * 60;
28+
pub const DAYS: BlockNumber = HOURS * 24;
2829

2930
/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
3031
/// used to limit the maximal weight of a single extrinsic.

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

Lines changed: 245 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ pub mod xcm_config;
1212

1313
use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
1414
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
15+
use pallet_transaction_payment::{Multiplier, MultiplierUpdate};
1516
use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery;
1617
use sp_api::impl_runtime_apis;
1718
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
1819
use sp_runtime::{
1920
create_runtime_str, generic, impl_opaque_keys,
20-
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
21+
traits::{
22+
AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, Convert, SaturatedConversion,
23+
Saturating,
24+
},
2125
transaction_validity::{TransactionSource, TransactionValidity},
22-
ApplyExtrinsicResult,
26+
ApplyExtrinsicResult, FixedPointNumber, Perquintill,
2327
};
2428

2529
use sp_std::prelude::*;
@@ -34,7 +38,7 @@ use frame_support::{
3438
dispatch::DispatchClass,
3539
genesis_builder_helper::{build_config, create_default_config},
3640
parameter_types,
37-
traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin},
41+
traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Get, TransformOrigin},
3842
weights::{ConstantMultiplier, Weight},
3943
PalletId,
4044
};
@@ -45,16 +49,19 @@ use frame_system::{
4549
use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
4650
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
4751

48-
use sugondat_primitives::{AccountId, AuraId, Balance, BlockNumber, Nonce, Signature};
52+
use sugondat_primitives::{
53+
AccountId, AuraId, Balance, BlockNumber, Nonce, Signature, MAXIMUM_BLOCK_LENGTH,
54+
};
4955

56+
use pallet_transaction_payment::TargetedFeeAdjustment;
5057
pub use sp_runtime::{MultiAddress, Perbill, Permill};
5158
use xcm_config::{KusamaLocation, XcmOriginToTransactDispatchOrigin};
5259

5360
#[cfg(any(feature = "std", test))]
5461
pub use sp_runtime::BuildStorage;
5562

5663
// Polkadot imports
57-
use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
64+
use polkadot_runtime_common::BlockHashCount;
5865

5966
use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
6067

@@ -142,7 +149,7 @@ parameter_types! {
142149
// `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize
143150
// the lazy contract deletion.
144151
pub RuntimeBlockLength: BlockLength =
145-
BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
152+
BlockLength::max_with_normal_ratio(MAXIMUM_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO);
146153
pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
147154
.base_block(BlockExecutionWeight::get())
148155
.for_class(DispatchClass::all(), |weights| {
@@ -267,14 +274,244 @@ impl pallet_balances::Config for Runtime {
267274
parameter_types! {
268275
/// Relay Chain `TransactionByteFee` / 10
269276
pub const TransactionByteFee: Balance = MILLICENTS;
277+
278+
// Common constants used in all runtimes for SlowAdjustingFeeUpdate
279+
280+
/// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less
281+
/// than this will decrease the weight and more will increase.
282+
pub storage TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
283+
284+
/// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
285+
/// change the fees more rapidly.
286+
pub AdjustmentVariableBlockFullness: Multiplier = Multiplier::saturating_from_rational(75, 1000_000);
287+
/// that combined with `AdjustmentVariable`, we can recover from the minimum.
288+
/// See `multiplier_can_grow_from_zero`.
289+
pub MinimumMultiplierBlockFullness: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
290+
/// The maximum amount of the multiplier.
291+
pub MaximumMultiplierBlockFullness: Multiplier = Bounded::max_value();
292+
293+
294+
// parameters used by BlobsFeeAdjustment
295+
296+
pub storage NextLengthMultiplier: Multiplier = Multiplier::saturating_from_integer(1);
297+
298+
pub storage TargetBlockSize: u32 = 820 * 1024; // 0.8MiB
299+
// TODO: update those value accordingly with https://github.com/thrumdev/blobs/issues/16
300+
pub AdjustmentVariableBlockSize: Multiplier = Multiplier::saturating_from_rational(75, 1000_000);
301+
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
302+
pub MaximumMultiplierBlockSize: Multiplier = Bounded::max_value();
303+
304+
// A positive number represents the count of consecutive blocks that exceeded
305+
// the TargetBlockSize, while a negative number represents the count of
306+
// consecutive blocks that were below the TargetBlockSize - NegativeDeltaTargetBlockFullness.
307+
pub storage BlockSizeTracker: u32 = 0; // 0.8MiB
308+
309+
// The number of consecutive blocks after which the TargetBlockSize will increase
310+
pub storage IncreaseDeltaBlocks: u32 = 10 * DAYS;
311+
312+
// The number of bytes that will be added to TargetBlockSize each update
313+
pub storage DeltaTargetBlockSize: u32 = 205 * 1024;// 0.2MiB
314+
315+
//pub storage DecreaseDeltaBlocks: u32 = 10 * DAYS;,
316+
//pub storage LowerBoundTargetBlockSize
317+
}
318+
319+
/// Parameterized slow adjusting fee updated based on
320+
/// <https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism>
321+
//pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
322+
// R,
323+
// TargetBlockFullness,
324+
// AdjustmentVariable,
325+
// MinimumMultiplier,
326+
// MaximumMultiplier,
327+
//>;
328+
329+
/// Currently pallet_transaction_payment use the following formula:
330+
///
331+
/// ```
332+
/// inclusion_fee = base_fee + length_fee + [targeted_fee_adjustment * weight_fee];
333+
/// ```
334+
///
335+
/// Letting us able to update `targeted_fee_adjustment` at the end of each block
336+
/// thanks to `FeeMultiplierUpdate`, this associated type is called inside the `on_finalize`
337+
/// of the transaction_payment pallet with the aim of converting the before `targeted_fee_adjustment`
338+
/// to a new one based on the congestion of the network
339+
///
340+
/// What this struct does is this PLUS a side effect, the goal is to reach a different formula to
341+
/// calculate fees:
342+
///
343+
/// ```
344+
/// inclusion_fee = base_fee + [targeted_length_fee_adjustment * length_fee] + [targeted_weight_fee_adjustment * weight_fee];
345+
/// ```
346+
///
347+
/// As you can see `targeted_fee_adjustment` becomes `targeted_weight_fee_adjustment` but the behavior
348+
/// remains the same, the side effect is the changing to the value `targeted_length_fee_adjustment`,
349+
/// this formula is achievable because inside pallet_transaction_payment the function `compute_fee_raw`
350+
/// that just computes the final fee associated with an extrinsic uses the associated type `LenghtToFee`
351+
/// that converts the length of an extrinsic to a fee.
352+
///
353+
/// By default the implementation is a constant multiplication but we want to achieve a dynamic formula
354+
/// that can adapt based on the usage of the network, this can't solely be done by this struct but needs
355+
/// to be bundled with a custom implementation of `LenghtToFee`.
356+
///
357+
/// This struct ONLY provide a dynamic update of `targeted_length_fee_adjustment` and `targeted_weight_fee_adjustment`
358+
/// based on the congestion and usage of the blocks, while the formula si effectively implemented like
359+
/// explained above only thanks to `LenghtToFee`
360+
pub struct BlobsFeeAdjustment<T: frame_system::Config>(core::marker::PhantomData<T>);
361+
362+
impl<T: frame_system::Config> Convert<Multiplier, Multiplier> for BlobsFeeAdjustment<T>
363+
where
364+
T: frame_system::Config,
365+
{
366+
/// This function should be a pure function used to update NextFeeMultiplier
367+
/// but will also has the side effect of update NextLenghtMultiplier
368+
fn convert(previous_fee_multiplier: Multiplier) -> Multiplier {
369+
// Update TargetBlockSize if needed
370+
let all_extrinsic_len = <frame_system::Pallet<T>>::all_extrinsics_len();
371+
372+
if all_extrinsic_len > TargetBlockSize::get() {
373+
// Increase the tracker if needed
374+
let tracker = BlockSizeTracker::get();
375+
376+
// If the used_block_size is larger than the TargetBlockSize
377+
// for more than IncreaseDeltaBlocks consecutive, then the TargetBlockSize
378+
// will be increased by DeltaTargetBlockSize.
379+
if tracker + 1 >= IncreaseDeltaBlocks::get() {
380+
let current_target_block_size = TargetBlockSize::get();
381+
TargetBlockSize::set(&(current_target_block_size + DeltaTargetBlockSize::get()));
382+
BlockSizeTracker::set(&0);
383+
}
384+
385+
BlockSizeTracker::set(&(tracker + 1));
386+
} else {
387+
// The logic for updating to a new TargetBlockSize currently requires IncreaseDeltaBlocks
388+
// to be constantly larger than TargetBlockSize. However, it might be possible
389+
// to implement a window where we reset the tracker only if the
390+
// block size remains below the TargetBlockSize for a certain number of blocks
391+
BlockSizeTracker::set(&0);
392+
}
393+
394+
// Update NextLengthMultiplier
395+
396+
// To update the value will be used the same formula as TargetedFeeAdjustment,
397+
// described here: https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism
398+
//
399+
// so this is mainly a copy paste of that function because it works on normalized mesurments,
400+
// so if it is ref_time, proof_size or lenght of the extrinsic the mutliplier will be evaluated properly.
401+
// The main problem is that TargetedFeeAdjustment::convert uses directly a call to the storage to extract
402+
// the weight of the current block so there is no way to pass the lenght as input argument,
403+
// here I will copy paste all the needed part to update properly NextLengthMultiplier
404+
405+
// Defensive only. The multiplier in storage should always be at most positive. Nonetheless
406+
// we recover here in case of errors, because any value below this would be stale and can
407+
// never change.
408+
409+
let previous_len_multiplier = NextLengthMultiplier::get();
410+
let min_multiplier = MinimumMultiplierBlockSize::get();
411+
let max_multiplier = MaximumMultiplierBlockSize::get();
412+
// TODO: why?
413+
let previous_len_multiplier = previous_len_multiplier.max(min_multiplier);
414+
415+
// Pick the limiting dimension. (from TargetedFeeAdjustment::convert)
416+
//
417+
// In this case it is the length of all extrinsic, always
418+
let (normal_limiting_dimension, max_limiting_dimension) =
419+
(all_extrinsic_len, MAXIMUM_BLOCK_LENGTH);
420+
421+
let target_block_size = TargetBlockSize::get();
422+
let adjustment_variable = AdjustmentVariableBlockSize::get();
423+
424+
let target_size = (target_block_size * max_limiting_dimension) as u128;
425+
let block_size = normal_limiting_dimension as u128;
426+
427+
// determines if the first_term is positive
428+
let positive = block_size >= target_size;
429+
let diff_abs = block_size.max(target_size) - block_size.min(target_size);
430+
431+
// defensive only, a test case assures that the maximum weight diff can fit in Multiplier
432+
// without any saturation.
433+
let diff = Multiplier::saturating_from_rational(diff_abs, max_limiting_dimension.max(1));
434+
let diff_squared = diff.saturating_mul(diff);
435+
436+
let v_squared_2 = adjustment_variable.saturating_mul(adjustment_variable)
437+
/ Multiplier::saturating_from_integer(2);
438+
439+
let first_term = adjustment_variable.saturating_mul(diff);
440+
let second_term = v_squared_2.saturating_mul(diff_squared);
441+
442+
let new_len_multiplier = if positive {
443+
let excess = first_term
444+
.saturating_add(second_term)
445+
.saturating_mul(previous_len_multiplier);
446+
previous_len_multiplier
447+
.saturating_add(excess)
448+
.clamp(min_multiplier, max_multiplier)
449+
} else {
450+
// Defensive-only: first_term > second_term. Safe subtraction.
451+
let negative = first_term
452+
.saturating_sub(second_term)
453+
.saturating_mul(previous_len_multiplier);
454+
previous_len_multiplier
455+
.saturating_sub(negative)
456+
.clamp(min_multiplier, max_multiplier)
457+
};
458+
459+
NextLengthMultiplier::set(&new_len_multiplier);
460+
461+
// Update NextFeeMultiplier
462+
//
463+
// Here is the tricky part, this method return the new value associated with
464+
// NextFeeMultiplier (in the old fashion) because weight dynamic adjustment is battle tested
465+
// while previously have updated the `NextLengthMultiplier` used in `LenghtToWeight`
466+
TargetedFeeAdjustment::<
467+
T,
468+
TargetBlockFullness,
469+
AdjustmentVariableBlockFullness,
470+
MinimumMultiplierBlockFullness,
471+
MaximumMultiplierBlockFullness,
472+
>::convert(previous_fee_multiplier)
473+
}
474+
}
475+
476+
impl<T: frame_system::Config> MultiplierUpdate for BlobsFeeAdjustment<T> {
477+
fn min() -> Multiplier {
478+
MinimumMultiplierBlockFullness::get()
479+
}
480+
fn max() -> Multiplier {
481+
MaximumMultiplierBlockFullness::get()
482+
}
483+
fn target() -> Perquintill {
484+
TargetBlockFullness::get()
485+
}
486+
fn variability() -> Multiplier {
487+
AdjustmentVariableBlockFullness::get()
488+
}
489+
}
490+
491+
pub struct BlobsLengthToFee<T: frame_system::Config>(core::marker::PhantomData<T>);
492+
493+
impl<T: frame_system::Config> sp_weights::WeightToFee for BlobsLengthToFee<T> {
494+
type Balance = Balance;
495+
496+
fn weight_to_fee(weight: &Weight) -> Self::Balance {
497+
// really weird but weght.ref_time will contain the length of the extrinsic
498+
let length_fee = Self::Balance::saturated_from(weight.ref_time())
499+
.saturating_mul(TransactionByteFee::get());
500+
let multiplier = NextLengthMultiplier::get();
501+
502+
// final adjusted length fee
503+
multiplier.saturating_mul_int(length_fee)
504+
}
270505
}
271506

272507
impl pallet_transaction_payment::Config for Runtime {
273508
type RuntimeEvent = RuntimeEvent;
274509
type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>;
275510
type WeightToFee = WeightToFee;
276-
type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
277-
type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
511+
//type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
512+
type LengthToFee = BlobsLengthToFee<Self>;
513+
//type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
514+
type FeeMultiplierUpdate = BlobsFeeAdjustment<Self>;
278515
type OperationalFeeMultiplier = ConstU8<5>;
279516
}
280517

0 commit comments

Comments
 (0)