Skip to content

Commit

Permalink
init blobs fees adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriele-0201 authored and rphmeier committed Dec 27, 2023
1 parent 7d87615 commit 1e8176e
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions sugondat-chain/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use sp_runtime::{
MultiSignature,
};

// TODO: probably this could be moved into runtimes/kusama-runtime/src/constants.rs
pub const MAXIMUM_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;

/// An index to a block.
pub type BlockNumber = u32;

Expand Down
4 changes: 3 additions & 1 deletion sugondat-chain/runtimes/sugondat-kusama/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ sp-session = { git = "https://github.com/paritytech/polkadot-sdk", default-featu
sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
sp-version = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
sp-weights = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}

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

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

[features]
Expand Down Expand Up @@ -132,6 +133,7 @@ std = [
"sp-transaction-pool/std",
"sp-version/std",
"sp-io/std",
"sp-weights/std",
"substrate-wasm-builder",
"xcm-builder/std",
"xcm-executor/std",
Expand Down
1 change: 1 addition & 0 deletions sugondat-chain/runtimes/sugondat-kusama/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod consensus {
// Time is measured by number of blocks.
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;

/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
/// used to limit the maximal weight of a single extrinsic.
Expand Down
253 changes: 245 additions & 8 deletions sugondat-chain/runtimes/sugondat-kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ pub mod xcm_config;

use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
use pallet_transaction_payment::{Multiplier, MultiplierUpdate};
use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery;
use sp_api::impl_runtime_apis;
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
traits::{
AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, Convert, SaturatedConversion,
Saturating,
},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
ApplyExtrinsicResult, FixedPointNumber, Perquintill,
};

use sp_std::prelude::*;
Expand All @@ -34,7 +38,7 @@ use frame_support::{
dispatch::DispatchClass,
genesis_builder_helper::{build_config, create_default_config},
parameter_types,
traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin},
traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Get, TransformOrigin},
weights::{ConstantMultiplier, Weight},
PalletId,
};
Expand All @@ -45,16 +49,19 @@ use frame_system::{
use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};

use sugondat_primitives::{AccountId, AuraId, Balance, BlockNumber, Nonce, Signature};
use sugondat_primitives::{
AccountId, AuraId, Balance, BlockNumber, Nonce, Signature, MAXIMUM_BLOCK_LENGTH,
};

use pallet_transaction_payment::TargetedFeeAdjustment;
pub use sp_runtime::{MultiAddress, Perbill, Permill};
use xcm_config::{KusamaLocation, XcmOriginToTransactDispatchOrigin};

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

// Polkadot imports
use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
use polkadot_runtime_common::BlockHashCount;

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

Expand Down Expand Up @@ -142,7 +149,7 @@ parameter_types! {
// `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize
// the lazy contract deletion.
pub RuntimeBlockLength: BlockLength =
BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
BlockLength::max_with_normal_ratio(MAXIMUM_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO);
pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
.base_block(BlockExecutionWeight::get())
.for_class(DispatchClass::all(), |weights| {
Expand Down Expand Up @@ -267,14 +274,244 @@ impl pallet_balances::Config for Runtime {
parameter_types! {
/// Relay Chain `TransactionByteFee` / 10
pub const TransactionByteFee: Balance = MILLICENTS;

// Common constants used in all runtimes for SlowAdjustingFeeUpdate

/// 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 storage TargetBlockFullness: Perquintill = Perquintill::from_percent(25);

/// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
/// change the fees more rapidly.
pub AdjustmentVariableBlockFullness: Multiplier = Multiplier::saturating_from_rational(75, 1000_000);
/// that combined with `AdjustmentVariable`, we can recover from the minimum.
/// See `multiplier_can_grow_from_zero`.
pub MinimumMultiplierBlockFullness: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
/// The maximum amount of the multiplier.
pub MaximumMultiplierBlockFullness: Multiplier = Bounded::max_value();


// parameters used by BlobsFeeAdjustment

pub storage NextLengthMultiplier: Multiplier = Multiplier::saturating_from_integer(1);

pub storage TargetBlockSize: u32 = 820 * 1024; // 0.8MiB
// TODO: update those value accordingly with https://github.com/thrumdev/blobs/issues/16
pub AdjustmentVariableBlockSize: Multiplier = Multiplier::saturating_from_rational(75, 1000_000);
pub MinimumMultiplierBlockSize: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
pub MaximumMultiplierBlockSize: Multiplier = Bounded::max_value();

// A positive number represents the count of consecutive blocks that exceeded
// the TargetBlockSize, while a negative number represents the count of
// consecutive blocks that were below the TargetBlockSize - NegativeDeltaTargetBlockFullness.
pub storage BlockSizeTracker: u32 = 0; // 0.8MiB

// The number of consecutive blocks after which the TargetBlockSize will increase
pub storage IncreaseDeltaBlocks: u32 = 10 * DAYS;

// The number of bytes that will be added to TargetBlockSize each update
pub storage DeltaTargetBlockSize: u32 = 205 * 1024;// 0.2MiB

//pub storage DecreaseDeltaBlocks: u32 = 10 * DAYS;,
//pub storage LowerBoundTargetBlockSize
}

/// Parameterized slow adjusting fee updated based on
/// <https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism>
//pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
// R,
// TargetBlockFullness,
// AdjustmentVariable,
// MinimumMultiplier,
// MaximumMultiplier,
//>;

/// Currently pallet_transaction_payment use the following formula:
///
/// ```
/// inclusion_fee = base_fee + length_fee + [targeted_fee_adjustment * weight_fee];
/// ```
///
/// Letting us able to update `targeted_fee_adjustment` at the end of each block
/// thanks to `FeeMultiplierUpdate`, this associated type is called inside the `on_finalize`
/// of the transaction_payment pallet with the aim of converting the before `targeted_fee_adjustment`
/// to a new one based on the congestion of the network
///
/// What this struct does is this PLUS a side effect, the goal is to reach a different formula to
/// calculate fees:
///
/// ```
/// inclusion_fee = base_fee + [targeted_length_fee_adjustment * length_fee] + [targeted_weight_fee_adjustment * weight_fee];
/// ```
///
/// As you can see `targeted_fee_adjustment` becomes `targeted_weight_fee_adjustment` but the behavior
/// remains the same, the side effect is the changing to the value `targeted_length_fee_adjustment`,
/// this formula is achievable because inside pallet_transaction_payment the function `compute_fee_raw`
/// that just computes the final fee associated with an extrinsic uses the associated type `LenghtToFee`
/// that converts the length of an extrinsic to a fee.
///
/// By default the implementation is a constant multiplication but we want to achieve a dynamic formula
/// that can adapt based on the usage of the network, this can't solely be done by this struct but needs
/// to be bundled with a custom implementation of `LenghtToFee`.
///
/// This struct ONLY provide a dynamic update of `targeted_length_fee_adjustment` and `targeted_weight_fee_adjustment`
/// based on the congestion and usage of the blocks, while the formula si effectively implemented like
/// explained above only thanks to `LenghtToFee`
pub struct BlobsFeeAdjustment<T: frame_system::Config>(core::marker::PhantomData<T>);

impl<T: frame_system::Config> Convert<Multiplier, Multiplier> for BlobsFeeAdjustment<T>
where
T: frame_system::Config,
{
/// This function should be a pure function used to update NextFeeMultiplier
/// but will also has the side effect of update NextLenghtMultiplier
fn convert(previous_fee_multiplier: Multiplier) -> Multiplier {
// Update TargetBlockSize if needed
let all_extrinsic_len = <frame_system::Pallet<T>>::all_extrinsics_len();

if all_extrinsic_len > TargetBlockSize::get() {
// Increase the tracker if needed
let tracker = BlockSizeTracker::get();

// If the used_block_size is larger than the TargetBlockSize
// for more than IncreaseDeltaBlocks consecutive, then the TargetBlockSize
// will be increased by DeltaTargetBlockSize.
if tracker + 1 >= IncreaseDeltaBlocks::get() {
let current_target_block_size = TargetBlockSize::get();
TargetBlockSize::set(&(current_target_block_size + DeltaTargetBlockSize::get()));
BlockSizeTracker::set(&0);
}

BlockSizeTracker::set(&(tracker + 1));
} else {
// The logic for updating to a new TargetBlockSize currently requires IncreaseDeltaBlocks
// to be constantly larger than TargetBlockSize. However, it might be possible
// to implement a window where we reset the tracker only if the
// block size remains below the TargetBlockSize for a certain number of blocks
BlockSizeTracker::set(&0);
}

// Update NextLengthMultiplier

// To update the value will be used the same formula as TargetedFeeAdjustment,
// described here: https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism
//
// so this is mainly a copy paste of that function because it works on normalized mesurments,
// so if it is ref_time, proof_size or lenght of the extrinsic the mutliplier will be evaluated properly.
// The main problem is that TargetedFeeAdjustment::convert uses directly a call to the storage to extract
// the weight of the current block so there is no way to pass the lenght as input argument,
// here I will copy paste all the needed part to update properly NextLengthMultiplier

// Defensive only. The multiplier in storage should always be at most positive. Nonetheless
// we recover here in case of errors, because any value below this would be stale and can
// never change.

let previous_len_multiplier = NextLengthMultiplier::get();
let min_multiplier = MinimumMultiplierBlockSize::get();
let max_multiplier = MaximumMultiplierBlockSize::get();
// TODO: why?
let previous_len_multiplier = previous_len_multiplier.max(min_multiplier);

// Pick the limiting dimension. (from TargetedFeeAdjustment::convert)
//
// In this case it is the length of all extrinsic, always
let (normal_limiting_dimension, max_limiting_dimension) =
(all_extrinsic_len, MAXIMUM_BLOCK_LENGTH);

let target_block_size = TargetBlockSize::get();
let adjustment_variable = AdjustmentVariableBlockSize::get();

let target_size = (target_block_size * max_limiting_dimension) as u128;
let block_size = normal_limiting_dimension as u128;

// determines if the first_term is positive
let positive = block_size >= target_size;
let diff_abs = block_size.max(target_size) - block_size.min(target_size);

// defensive only, a test case assures that the maximum weight diff can fit in Multiplier
// without any saturation.
let diff = Multiplier::saturating_from_rational(diff_abs, max_limiting_dimension.max(1));
let diff_squared = diff.saturating_mul(diff);

let v_squared_2 = adjustment_variable.saturating_mul(adjustment_variable)
/ Multiplier::saturating_from_integer(2);

let first_term = adjustment_variable.saturating_mul(diff);
let second_term = v_squared_2.saturating_mul(diff_squared);

let new_len_multiplier = if positive {
let excess = first_term
.saturating_add(second_term)
.saturating_mul(previous_len_multiplier);
previous_len_multiplier
.saturating_add(excess)
.clamp(min_multiplier, max_multiplier)
} else {
// Defensive-only: first_term > second_term. Safe subtraction.
let negative = first_term
.saturating_sub(second_term)
.saturating_mul(previous_len_multiplier);
previous_len_multiplier
.saturating_sub(negative)
.clamp(min_multiplier, max_multiplier)
};

NextLengthMultiplier::set(&new_len_multiplier);

// Update NextFeeMultiplier
//
// Here is the tricky part, this method return the new value associated with
// NextFeeMultiplier (in the old fashion) because weight dynamic adjustment is battle tested
// while previously have updated the `NextLengthMultiplier` used in `LenghtToWeight`
TargetedFeeAdjustment::<
T,
TargetBlockFullness,
AdjustmentVariableBlockFullness,
MinimumMultiplierBlockFullness,
MaximumMultiplierBlockFullness,
>::convert(previous_fee_multiplier)
}
}

impl<T: frame_system::Config> MultiplierUpdate for BlobsFeeAdjustment<T> {
fn min() -> Multiplier {
MinimumMultiplierBlockFullness::get()
}
fn max() -> Multiplier {
MaximumMultiplierBlockFullness::get()
}
fn target() -> Perquintill {
TargetBlockFullness::get()
}
fn variability() -> Multiplier {
AdjustmentVariableBlockFullness::get()
}
}

pub struct BlobsLengthToFee<T: frame_system::Config>(core::marker::PhantomData<T>);

impl<T: frame_system::Config> sp_weights::WeightToFee for BlobsLengthToFee<T> {
type Balance = Balance;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
// really weird but weght.ref_time will contain the length of the extrinsic
let length_fee = Self::Balance::saturated_from(weight.ref_time())
.saturating_mul(TransactionByteFee::get());
let multiplier = NextLengthMultiplier::get();

// final adjusted length fee
multiplier.saturating_mul_int(length_fee)
}
}

impl pallet_transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>;
type WeightToFee = WeightToFee;
type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
//type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
type LengthToFee = BlobsLengthToFee<Self>;
//type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
type FeeMultiplierUpdate = BlobsFeeAdjustment<Self>;
type OperationalFeeMultiplier = ConstU8<5>;
}

Expand Down
Loading

0 comments on commit 1e8176e

Please sign in to comment.