Skip to content

Commit b4b8fff

Browse files
gabriele-0201rphmeier
authored andcommitted
extract fee adjustment into new pallet
1 parent 405163d commit b4b8fff

File tree

8 files changed

+284
-852
lines changed

8 files changed

+284
-852
lines changed

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[package]
2+
name = "pallet-sugondat-length-fee-adjustment"
3+
version = "0.1.0"
4+
authors = ["Anonymous"]
5+
description = "Pallet for fee Adjustment based on block length and weight"
6+
license = "MIT OR Apache-2.0"
7+
repository = "https://github.com/thrumdev/blobs"
8+
edition = "2021"
9+
10+
[package.metadata.docs.rs]
11+
targets = ["x86_64-unknown-linux-gnu"]
12+
13+
[dependencies]
14+
codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false }
15+
scale-info = { version = "2.2.0", default-features = false, features = ["derive"] }
16+
frame-support = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
17+
frame-system = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
18+
pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
19+
pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
20+
sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
21+
sp-weights = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
22+
sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
23+
24+
[dev-dependencies]
25+
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
26+
sp-core = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
27+
28+
[features]
29+
default = [ "std" ]
30+
std = [
31+
"codec/std",
32+
"frame-support/std",
33+
"frame-system/std",
34+
"sp-runtime/std",
35+
"sp-weights/std",
36+
"scale-info/std",
37+
"sp-arithmetic/std",
38+
"pallet-transaction-payment/std",
39+
"pallet-balances/std",
40+
]
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#![cfg_attr(not(feature = "std"), no_std)]
2+
3+
pub use pallet::*;
4+
5+
/// Currently, the `pallet_transaction_payment` uses the following formula:
6+
///
7+
/// ```ignore
8+
/// inclusion_fee = base_fee + length_fee + [targeted_fee_adjustment * weight_fee];
9+
/// ```
10+
///
11+
/// This formula allows us to update `targeted_fee_adjustment` at the end of each block
12+
/// using `FeeMultiplierUpdate`, this associated type is called within the `on_finalize`
13+
/// function of the `transaction_payment` pallet, with the purpose of converting the existing
14+
/// `targeted_fee_adjustment` to a new one based on network congestion.
15+
///
16+
/// The goal of this pallet is to achieve a modified fee calculation formula:
17+
///
18+
/// ```ignore
19+
/// inclusion_fee = base_fee + [targeted_length_fee_adjustment * length_fee] + [targeted_weight_fee_adjustment * weight_fee];
20+
/// ```
21+
///
22+
/// `targeted_fee_adjustment` becomes `targeted_weight_fee_adjustment`,
23+
/// while the behavior remains the same.
24+
/// `targeted_length_fee_adjustment` is a new multiplier associate to `length_fee`.
25+
/// This formula is achievable because the `transaction_payment`
26+
/// pallet uses the `compute_fee_raw` function, which computes the final fee associated with an
27+
/// extrinsic. This function utilizes the associated type `LengthToFee`, which converts the length
28+
/// of an extrinsic to a fee.
29+
///
30+
/// By default, the implementation of `LengthToFee` is a constant multiplication. However, we
31+
/// aim to achieve a dynamic formula, thanks to the new multiplier stored in `NextLenghtMultiplier`, implementing `sp_weights::WeightToFee`
32+
/// for the Pallet struct and thus being able to use it as value for the associated type `LengthToFee` in the
33+
/// `pallet_transaction_type::Config`.
34+
///
35+
/// `targeted_length_fee_adjustment` is updated at the end of each block inside `on_finalize`
36+
#[frame_support::pallet]
37+
pub mod pallet {
38+
39+
use frame_support::pallet_prelude::*;
40+
use frame_system::pallet_prelude::*;
41+
use pallet_transaction_payment::{Multiplier, OnChargeTransaction};
42+
use sp_runtime::{traits::Get, FixedPointNumber, Perquintill, SaturatedConversion, Saturating};
43+
44+
/// Configure the pallet by specifying the parameters and types on which it depends.
45+
#[pallet::config]
46+
pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
47+
// `targeted_length_fee_adjustment` parameters
48+
#[pallet::constant]
49+
type TransactionByteFee: Get<<<Self as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<Self>>::Balance>;
50+
#[pallet::constant]
51+
type MaximumBlockLength: Get<u32>;
52+
#[pallet::constant]
53+
type AdjustmentVariableBlockSize: Get<Multiplier>;
54+
#[pallet::constant]
55+
type MinimumMultiplierBlockSize: Get<Multiplier>;
56+
#[pallet::constant]
57+
type MaximumMultiplierBlockSize: Get<Multiplier>;
58+
}
59+
60+
#[pallet::pallet]
61+
pub struct Pallet<T>(_);
62+
63+
pub struct NextLengthMultiplierDefualt;
64+
impl Get<Multiplier> for NextLengthMultiplierDefualt {
65+
fn get() -> Multiplier {
66+
Multiplier::saturating_from_integer(1)
67+
}
68+
}
69+
70+
#[pallet::storage]
71+
pub type NextLengthMultiplier<T: Config> =
72+
StorageValue<_, Multiplier, ValueQuery, NextLengthMultiplierDefualt>;
73+
74+
pub struct TargetBlockSizeDefault;
75+
impl Get<Perquintill> for TargetBlockSizeDefault {
76+
fn get() -> Perquintill {
77+
Perquintill::from_percent(16) // 0.8MiB
78+
}
79+
}
80+
81+
#[pallet::storage]
82+
pub type TargetBlockSize<T: Config> =
83+
StorageValue<_, Perquintill, ValueQuery, TargetBlockSizeDefault>;
84+
85+
#[pallet::hooks]
86+
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
87+
fn on_initialize(_: BlockNumberFor<T>) -> Weight {
88+
// TODO: implement skip block logic
89+
// https://github.com/thrumdev/blobs/issues/165
90+
91+
// NextLengthMultiplier: 1r + 1w
92+
// TargetBlockSize: 1r
93+
T::DbWeight::get().reads_writes(2, 1)
94+
}
95+
96+
fn on_finalize(_n: BlockNumberFor<T>) {
97+
// update targeted_weight_fee_adjustment,
98+
// contained in NextLengthMultiplier storage item
99+
100+
// This is essentially a copy-paste of the function TargetedFeeAdjustment::convert.
101+
// The main problem is that TargetedFeeAdjustment::convert directly calls the storage to extract the weight
102+
// of the current block, so there is no way to pass the length as an input argument and reuse the function to
103+
// update also the length multiplier.
104+
// Therefore, all the necessary parts taken and properly adapted to update NextLengthMultiplier.
105+
106+
// Defensive only. The multiplier in storage should always be at most positive. Nonetheless
107+
// we recover here in case of errors, because any value below this would be stale and can
108+
// never change.
109+
let previous_len_multiplier = NextLengthMultiplier::<T>::get();
110+
let min_multiplier = T::MinimumMultiplierBlockSize::get();
111+
let max_multiplier = T::MaximumMultiplierBlockSize::get();
112+
let previous_len_multiplier = previous_len_multiplier.max(min_multiplier);
113+
114+
// The limiting dimension is the length of all extrinsic
115+
let (normal_limiting_dimension, max_limiting_dimension) = (
116+
<frame_system::Pallet<T>>::all_extrinsics_len().min(T::MaximumBlockLength::get()),
117+
T::MaximumBlockLength::get() as u64,
118+
);
119+
120+
let target_block_size = TargetBlockSize::<T>::get();
121+
let adjustment_variable = T::AdjustmentVariableBlockSize::get();
122+
123+
let target_size = (target_block_size * max_limiting_dimension) as u128;
124+
let block_size = normal_limiting_dimension as u128;
125+
126+
// determines if the first_term is positive
127+
let positive = block_size >= target_size;
128+
let diff_abs = block_size.max(target_size) - block_size.min(target_size);
129+
130+
// defensive only, a test case assures that the maximum weight diff can fit in Multiplier
131+
// without any saturation.
132+
let diff =
133+
Multiplier::saturating_from_rational(diff_abs, max_limiting_dimension.max(1));
134+
let diff_squared = diff.saturating_mul(diff);
135+
136+
let v_squared_2 = adjustment_variable.saturating_mul(adjustment_variable)
137+
/ Multiplier::saturating_from_integer(2);
138+
139+
let first_term = adjustment_variable.saturating_mul(diff);
140+
let second_term = v_squared_2.saturating_mul(diff_squared);
141+
142+
let new_len_multiplier = if positive {
143+
let excess = first_term
144+
.saturating_add(second_term)
145+
.saturating_mul(previous_len_multiplier);
146+
previous_len_multiplier
147+
.saturating_add(excess)
148+
.clamp(min_multiplier, max_multiplier)
149+
} else {
150+
// Defensive-only: first_term > second_term. Safe subtraction.
151+
let negative = first_term
152+
.saturating_sub(second_term)
153+
.saturating_mul(previous_len_multiplier);
154+
previous_len_multiplier
155+
.saturating_sub(negative)
156+
.clamp(min_multiplier, max_multiplier)
157+
};
158+
159+
NextLengthMultiplier::<T>::put(new_len_multiplier);
160+
}
161+
}
162+
163+
impl<T: Config + pallet_transaction_payment::Config> sp_weights::WeightToFee for Pallet<T> {
164+
type Balance = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
165+
166+
fn weight_to_fee(weight: &Weight) -> Self::Balance {
167+
// really weird but weight.ref_time will contain the length of the extrinsic
168+
let length_fee = Self::Balance::saturated_from(weight.ref_time())
169+
.saturating_mul(T::TransactionByteFee::get());
170+
let multiplier = NextLengthMultiplier::<T>::get();
171+
172+
// final adjusted length fee
173+
multiplier.saturating_mul_int(length_fee)
174+
}
175+
}
176+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ smallvec = "1.10.0"
2222

2323
# Local
2424
pallet-sugondat-blobs = { path = "../../pallets/blobs", default-features = false }
25+
pallet-sugondat-length-fee-adjustment = { path = "../../pallets/length-fee-adjustment", default-features = false }
2526
sugondat-primitives = { path = "../../primitives", default-features = false }
2627

2728
# Substrate
@@ -107,6 +108,7 @@ std = [
107108
"pallet-collator-selection/std",
108109
"pallet-message-queue/std",
109110
"pallet-sugondat-blobs/std",
111+
"pallet-sugondat-length-fee-adjustment/std",
110112
"pallet-session/std",
111113
"pallet-sudo/std",
112114
"pallet-timestamp/std",

0 commit comments

Comments
 (0)