Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Parachains should charge for proof size weight #2326

Merged
merged 9 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions parachains/common/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::{marker::PhantomData, ops::ControlFlow};
use frame_support::{
log,
traits::{fungibles::Inspect, tokens::BalanceConversion, ContainsPair},
weights::{Weight, WeightToFee, WeightToFeePolynomial},
weights::Weight,
};
use sp_runtime::traits::Get;
use xcm::{latest::prelude::*, CreateMatcher, MatchXcm};
Expand Down Expand Up @@ -95,7 +95,7 @@ impl<CurrencyBalance, Runtime, WeightToFee, BalanceConverter, AssetInstance>
> for AssetFeeAsExistentialDepositMultiplier<Runtime, WeightToFee, BalanceConverter, AssetInstance>
where
Runtime: pallet_assets::Config<AssetInstance>,
WeightToFee: WeightToFeePolynomial<Balance = CurrencyBalance>,
WeightToFee: frame_support::weights::WeightToFee<Balance = CurrencyBalance>,
BalanceConverter: BalanceConversion<
CurrencyBalance,
<Runtime as pallet_assets::Config<AssetInstance>>::AssetId,
Expand Down
3 changes: 2 additions & 1 deletion parachains/runtimes/assets/statemine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", default-fea
pallet-multisig = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-proxy = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-state-trie-migration = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master", optional = true }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
Expand All @@ -44,7 +45,7 @@ sp-session = { git = "https://github.com/paritytech/substrate", default-features
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-state-trie-migration = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master", optional = true }
sp-weights = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }

# Polkadot
kusama-runtime-constants = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
Expand Down
38 changes: 35 additions & 3 deletions parachains/runtimes/assets/statemine/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ pub mod currency {
/// Fee-related.
pub mod fee {
use frame_support::weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
constants::ExtrinsicBaseWeight, FeePolynomial, Weight, WeightToFeeCoefficient,
WeightToFeeCoefficients, WeightToFeePolynomial,
};
use polkadot_core_primitives::Balance;
use smallvec::smallvec;
Expand All @@ -55,13 +55,45 @@ pub mod fee {
/// - Setting it to `0` will essentially disable the weight fee.
/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
impl frame_support::weights::WeightToFee for WeightToFee {
type Balance = Balance;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
let time_poly: FeePolynomial<Balance> = RefTimeToFee::polynomial().into();
let proof_poly: FeePolynomial<Balance> = ProofSizeToFee::polynomial().into();

time_poly.eval(weight.ref_time()).max(proof_poly.eval(weight.proof_size()))
ggwpez marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Maps the reference time component of `Weight` to a fee.
pub struct RefTimeToFee;
impl WeightToFeePolynomial for RefTimeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// in Kusama, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT:
// in Statemine, we map to 1/10 of that, or 1/100 CENT
let p = super::currency::CENTS;
let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time());

smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p % q, q),
coeff_integer: p / q,
}]
}
}

/// Maps the proof size component of `Weight` to a fee.
pub struct ProofSizeToFee;
impl WeightToFeePolynomial for ProofSizeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// Map 10kb proof to 1 CENT.
let p = super::currency::CENTS;
let q = 10_000;
Comment on lines +94 to +96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we don't have a reference on the Relay Chain.

Copy link
Member Author

@ggwpez ggwpez Mar 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since the relay chain has u64::MAX as max block proof-size whereas parachains only have 5 MiB.
We will probably need some further adjustments to find the sweet-spot, but for now this should provide something that we can work with.

There are going to be changes in Substrate soon that increase the proof size of everything (until we have https://github.com/paritytech/substrate/issues/13501), so to prevent accidental fuckups i added the tests that 1K transfers fit in a block.


smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
Expand Down
56 changes: 56 additions & 0 deletions parachains/runtimes/assets/statemine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,3 +1117,59 @@ fn ensure_key_ss58() {
assert_eq!(acc, RootMigController::sorted_members()[0]);
//panic!("{:x?}", acc);
}

#[cfg(test)]
mod tests {
use super::{constants::fee, *};
use crate::{CENTS, MILLICENTS};
use sp_runtime::traits::Zero;
use sp_weights::WeightToFee;

/// We can fit at least 1000 transfers in a block.
#[test]
fn sane_block_weight() {
use pallet_balances::WeightInfo;
let block = RuntimeBlockWeights::get().max_block;
let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic;
let transfer = base + weights::pallet_balances::WeightInfo::<Runtime>::transfer();

let fit = block.checked_div_per_component(&transfer).unwrap_or_default();
assert!(fit >= 1000, "{} should be at least 1000", fit);
}

/// The fee for one transfer is at most 1 CENT.
#[test]
fn sane_transfer_fee() {
use pallet_balances::WeightInfo;
let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic;
let transfer = base + weights::pallet_balances::WeightInfo::<Runtime>::transfer();

let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer);
assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS);
}

/// Weight is being charged for both dimensions.
#[test]
fn weight_charged_for_both_components() {
let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0));
assert!(!fee.is_zero(), "Charges for ref time");

let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000));
assert_eq!(fee, CENTS, "10kb maps to CENT");
}

/// Filling up a block by proof size is at most 30 times more expensive than ref time.
///
/// This is just a sanity check.
#[test]
fn full_block_fee_ratio() {
let block = RuntimeBlockWeights::get().max_block;
let time_fee: Balance = fee::WeightToFee::weight_to_fee(&block.without_proof_size());
let proof_fee: Balance = fee::WeightToFee::weight_to_fee(&block.without_ref_time());

let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default();
assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time);
let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default();
assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof);
}
}
1 change: 1 addition & 0 deletions parachains/runtimes/assets/statemint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ sp-session = { git = "https://github.com/paritytech/substrate", default-features
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-weights = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }

# Polkadot
pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
Expand Down
39 changes: 36 additions & 3 deletions parachains/runtimes/assets/statemint/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ pub mod currency {
/// Fee-related.
pub mod fee {
use frame_support::weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
constants::ExtrinsicBaseWeight, FeePolynomial, WeightToFeeCoefficient,
WeightToFeeCoefficients, WeightToFeePolynomial,
};
use polkadot_core_primitives::Balance;
use smallvec::smallvec;
pub use sp_runtime::Perbill;
use sp_weights::Weight;

/// The block saturation level. Fees will be updates based on this value.
pub const TARGET_BLOCK_FULLNESS: Perbill = Perbill::from_percent(25);
Expand All @@ -55,13 +56,45 @@ pub mod fee {
/// - Setting it to `0` will essentially disable the weight fee.
/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
impl frame_support::weights::WeightToFee for WeightToFee {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be simplified by implementing WeightToFee for Tuple? or too many tuples impls? 🙈

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we could do a simple maximum norm. In the long run i would rather make the traits 2D weight compatible in Substrate, so we wont need it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now it is fine or? Since this targets 0.9.38.1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah nah, you would be confused if it is addition, or max or what.

I suppose we can provide some nice helpers at he substrate layer though:

struct RefTimeWeightCostPerMicroSec<Balance, Cost: Get<Balance>>(..);
impl WeightToFee for RefTimeConstPerMicroSec { .. }

struct ProofSizeWeightCostPerKByte<Balance, Cost: Get<Balance>>(..);
impl WeightToFee for RefTimeConstPerMicroSec { .. }

Or even a more generic:

struct CostPerUnit<Balance, Cost: Get<Balance>, Unit: ops::Div<Balance>>; 

I think you can see where I am going with this. Just an idea

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now it is fine or? Since this targets 0.9.38.1

Yeah fine for now. Perhaps a follow-up issue that you can mentor?

type Balance = Balance;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
let time_poly: FeePolynomial<Balance> = RefTimeToFee::polynomial().into();
let proof_poly: FeePolynomial<Balance> = ProofSizeToFee::polynomial().into();

time_poly.eval(weight.ref_time()).max(proof_poly.eval(weight.proof_size()))
}
}

/// Maps the reference time component of `Weight` to a fee.
pub struct RefTimeToFee;
impl WeightToFeePolynomial for RefTimeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT:
// in Statemint, we map to 1/10 of that, or 1/100 CENT
let p = super::currency::CENTS;
let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time());

smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p % q, q),
coeff_integer: p / q,
}]
}
}

/// Maps the proof size component of `Weight` to a fee.
pub struct ProofSizeToFee;
impl WeightToFeePolynomial for ProofSizeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// Map 10kb proof to 1 CENT.
let p = super::currency::CENTS;
let q = 10_000;

smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
Expand Down
56 changes: 56 additions & 0 deletions parachains/runtimes/assets/statemint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,3 +1069,59 @@ cumulus_pallet_parachain_system::register_validate_block! {
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
CheckInherents = CheckInherents,
}

#[cfg(test)]
mod tests {
use super::{constants::fee, *};
use crate::{CENTS, MILLICENTS};
use sp_runtime::traits::Zero;
use sp_weights::WeightToFee;

/// We can fit at least 1000 transfers in a block.
#[test]
fn sane_block_weight() {
use pallet_balances::WeightInfo;
let block = RuntimeBlockWeights::get().max_block;
let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic;
let transfer = base + weights::pallet_balances::WeightInfo::<Runtime>::transfer();

let fit = block.checked_div_per_component(&transfer).unwrap_or_default();
assert!(fit >= 1000, "{} should be at least 1000", fit);
}

/// The fee for one transfer is at most 1 CENT.
#[test]
fn sane_transfer_fee() {
use pallet_balances::WeightInfo;
let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic;
let transfer = base + weights::pallet_balances::WeightInfo::<Runtime>::transfer();

let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer);
assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS);
}

/// Weight is being charged for both dimensions.
#[test]
fn weight_charged_for_both_components() {
let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0));
assert!(!fee.is_zero(), "Charges for ref time");

let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000));
assert_eq!(fee, CENTS, "10kb maps to CENT");
}

/// Filling up a block by proof size is at most 30 times more expensive than ref time.
///
/// This is just a sanity check.
#[test]
fn full_block_fee_ratio() {
let block = RuntimeBlockWeights::get().max_block;
let time_fee: Balance = fee::WeightToFee::weight_to_fee(&block.without_proof_size());
let proof_fee: Balance = fee::WeightToFee::weight_to_fee(&block.without_ref_time());

let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default();
assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time);
let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default();
assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof);
}
}
47 changes: 41 additions & 6 deletions parachains/runtimes/assets/westmint/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ pub mod currency {

/// Fee-related.
pub mod fee {
use frame_support::weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
use frame_support::{
pallet_prelude::Weight,
weights::{
constants::ExtrinsicBaseWeight, FeePolynomial, WeightToFeeCoefficient,
WeightToFeeCoefficients, WeightToFeePolynomial,
},
};
use polkadot_core_primitives::Balance;
use smallvec::smallvec;
Expand All @@ -55,13 +58,45 @@ pub mod fee {
/// - Setting it to `0` will essentially disable the weight fee.
/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
impl frame_support::weights::WeightToFee for WeightToFee {
type Balance = Balance;

fn weight_to_fee(weight: &Weight) -> Self::Balance {
let time_poly: FeePolynomial<Balance> = RefTimeToFee::polynomial().into();
let proof_poly: FeePolynomial<Balance> = ProofSizeToFee::polynomial().into();

time_poly.eval(weight.ref_time()).max(proof_poly.eval(weight.proof_size()))
}
}

/// Maps the reference time component of `Weight` to a fee.
pub struct RefTimeToFee;
impl WeightToFeePolynomial for RefTimeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT:
// in Statemint, we map to 1/10 of that, or 1/100 CENT
// in Westend, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT:
// in Westmint, we map to 1/10 of that, or 1/100 CENT
let p = super::currency::CENTS;
let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time());

smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p % q, q),
coeff_integer: p / q,
}]
}
}

/// Maps the proof size component of `Weight` to a fee.
pub struct ProofSizeToFee;
impl WeightToFeePolynomial for ProofSizeToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// Map 10kb proof to 1 CENT.
let p = super::currency::CENTS;
let q = 10_000;

smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
Expand Down
Loading