Skip to content

Commit

Permalink
Merge pull request #2801 from subspace/extrinsic_weight
Browse files Browse the repository at this point in the history
Domains: Limit domain's max extrinsic weight to max domain bundle weight
  • Loading branch information
vedhavyas authored May 30, 2024
2 parents cee3bd6 + 5d1a0cf commit 447299b
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 86 deletions.
33 changes: 3 additions & 30 deletions crates/pallet-domains/src/domain_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ use frame_system::pallet_prelude::*;
use scale_info::TypeInfo;
use sp_core::Get;
use sp_domains::{
derive_domain_block_hash, DomainBundleLimit, DomainId, DomainsDigestItem,
DomainsTransfersTracker, OnDomainInstantiated, OperatorAllowList, RuntimeId, RuntimeType,
calculate_max_bundle_weight_and_size, derive_domain_block_hash, DomainBundleLimit, DomainId,
DomainsDigestItem, DomainsTransfersTracker, OnDomainInstantiated, OperatorAllowList, RuntimeId,
RuntimeType,
};
use sp_runtime::traits::{CheckedAdd, Zero};
use sp_runtime::DigestItem;
Expand Down Expand Up @@ -317,34 +318,6 @@ pub(crate) fn do_update_domain_allow_list<T: Config>(
})
}

// See https://forum.subspace.network/t/on-bundle-weight-limits-sum/2277 for more details
// about the formula
pub(crate) fn calculate_max_bundle_weight_and_size(
max_domain_block_size: u32,
max_domain_block_weight: Weight,
consensus_slot_probability: (u64, u64),
bundle_slot_probability: (u64, u64),
) -> Option<DomainBundleLimit> {
// (n1 / d1) / (n2 / d2) is equal to (n1 * d2) / (d1 * n2)
// This represents: bundle_slot_probability/SLOT_PROBABILITY
let expected_bundles_per_block = bundle_slot_probability
.0
.checked_mul(consensus_slot_probability.1)?
.checked_div(
bundle_slot_probability
.1
.checked_mul(consensus_slot_probability.0)?,
)?;

let max_bundle_weight = max_domain_block_weight.checked_div(expected_bundles_per_block)?;
let max_bundle_size = (max_domain_block_size as u64).checked_div(expected_bundles_per_block)?;

Some(DomainBundleLimit {
max_bundle_size: max_bundle_size as u32,
max_bundle_weight,
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
24 changes: 23 additions & 1 deletion crates/sp-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionErro
use core::num::ParseIntError;
use core::ops::{Add, Sub};
use core::str::FromStr;
use domain_runtime_primitives::MultiAccountId;
use domain_runtime_primitives::{calculate_max_bundle_weight, MultiAccountId};
use frame_support::storage::storage_prefix;
use frame_support::{Blake2_128Concat, StorageHasher};
use hexlit::hex;
Expand Down Expand Up @@ -1032,6 +1032,28 @@ pub struct DomainBundleLimit {
pub max_bundle_weight: Weight,
}

/// Calculates the max bundle weight and size
// See https://forum.subspace.network/t/on-bundle-weight-limits-sum/2277 for more details
// about the formula
pub fn calculate_max_bundle_weight_and_size(
max_domain_block_size: u32,
max_domain_block_weight: Weight,
consensus_slot_probability: (u64, u64),
bundle_slot_probability: (u64, u64),
) -> Option<DomainBundleLimit> {
let (expected_bundles_per_block, max_bundle_weight) = calculate_max_bundle_weight(
max_domain_block_weight,
consensus_slot_probability,
bundle_slot_probability,
)?;
let max_bundle_size = (max_domain_block_size as u64).checked_div(expected_bundles_per_block)?;

Some(DomainBundleLimit {
max_bundle_size: max_bundle_size as u32,
max_bundle_weight,
})
}

/// Checks if the signer Id hash is within the tx range
pub fn signer_in_tx_range(bundle_vrf_hash: &U256, signer_id_hash: &U256, tx_range: &U256) -> bool {
let distance_from_vrf_hash = bidirectional_distance(bundle_vrf_hash, signer_id_hash);
Expand Down
4 changes: 2 additions & 2 deletions crates/subspace-runtime-primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] }
frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "6da3c45e1d5b3c1f09b5e54152b8848149f9d5e6" }
frame-system = { default-features = false, optional = true, git = "https://github.com/subspace/polkadot-sdk", rev = "6da3c45e1d5b3c1f09b5e54152b8848149f9d5e6" }
frame-system = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "6da3c45e1d5b3c1f09b5e54152b8848149f9d5e6" }
pallet-transaction-payment = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "6da3c45e1d5b3c1f09b5e54152b8848149f9d5e6" }
scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "6da3c45e1d5b3c1f09b5e54152b8848149f9d5e6" }
Expand All @@ -31,13 +31,13 @@ default = ["std"]
std = [
"codec/std",
"frame-support/std",
"frame-system/std",
"pallet-transaction-payment/std",
"scale-info/std",
"sp-core/std",
"sp-runtime/std",
"subspace-core-primitives/std",
]
testing = [
"frame-system",
"sp-io"
]
21 changes: 20 additions & 1 deletion crates/subspace-runtime-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use codec::{Codec, Decode, Encode};
use frame_support::pallet_prelude::Weight;
use frame_support::traits::tokens;
use frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND;
use frame_system::limits::BlockLength;
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
use scale_info::TypeInfo;
use sp_core::parameter_types;
use sp_runtime::traits::{Bounded, IdentifyAccount, Verify};
use sp_runtime::{FixedPointNumber, MultiSignature, Perquintill};
use sp_runtime::{FixedPointNumber, MultiSignature, Perbill, Perquintill};
pub use subspace_core_primitives::BlockNumber;

/// Minimum desired number of replicas of the blockchain to be stored by the network,
Expand All @@ -42,6 +45,22 @@ pub const SHANNON: Balance = 1;
pub const DECIMAL_PLACES: u8 = 18;
/// One Subspace Credit.
pub const SSC: Balance = (10 * SHANNON).pow(DECIMAL_PLACES as u32);
/// A ratio of `Normal` dispatch class within block, for `BlockWeight` and `BlockLength`.
pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
/// 1 in 6 slots (on average, not counting collisions) will have a block.
/// Must match ratio between block and slot duration in constants above.
pub const SLOT_PROBABILITY: (u64, u64) = (1, 6);
/// The block weight for 2 seconds of compute
pub const BLOCK_WEIGHT_FOR_2_SEC: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);

/// Maximum block length for non-`Normal` extrinsic is 5 MiB.
pub const MAX_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;

/// We allow for 3.75 MiB for `Normal` extrinsic with 5 MiB maximum block length.
pub fn maximum_normal_block_length() -> BlockLength {
BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO)
}

/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
Expand Down
30 changes: 9 additions & 21 deletions crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ use codec::{Decode, Encode, MaxEncodedLen};
use core::num::NonZeroU64;
use domain_runtime_primitives::opaque::Header as DomainHeader;
use domain_runtime_primitives::{
AccountIdConverter, BlockNumber as DomainNumber, Hash as DomainHash,
maximum_domain_block_weight, AccountIdConverter, BlockNumber as DomainNumber,
Hash as DomainHash,
};
use frame_support::genesis_builder_helper::{build_config, create_default_config};
use frame_support::inherent::ProvideInherent;
use frame_support::traits::{
ConstU16, ConstU32, ConstU64, ConstU8, Currency, Everything, Get, OnRuntimeUpgrade,
VariantCount,
};
use frame_support::weights::constants::{ParityDbWeight, WEIGHT_REF_TIME_PER_SECOND};
use frame_support::weights::constants::ParityDbWeight;
use frame_support::weights::{ConstantMultiplier, IdentityFee, Weight};
use frame_support::{construct_runtime, parameter_types, PalletId};
use frame_system::limits::{BlockLength, BlockWeights};
Expand Down Expand Up @@ -82,7 +83,7 @@ use sp_runtime::traits::{
};
use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity};
use sp_runtime::{
create_runtime_str, generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill,
create_runtime_str, generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode,
};
use sp_std::collections::btree_map::BTreeMap;
use sp_std::marker::PhantomData;
Expand All @@ -97,8 +98,9 @@ use subspace_core_primitives::{
SegmentCommitment, SegmentHeader, SegmentIndex, SlotNumber, SolutionRange, U256,
};
use subspace_runtime_primitives::{
AccountId, Balance, BlockNumber, FindBlockRewardAddress, Hash, Moment, Nonce, Signature,
SlowAdjustingFeeUpdate, MIN_REPLICATION_FACTOR, SHANNON, SSC,
maximum_normal_block_length, AccountId, Balance, BlockNumber, FindBlockRewardAddress, Hash,
Moment, Nonce, Signature, SlowAdjustingFeeUpdate, BLOCK_WEIGHT_FOR_2_SEC, MAX_BLOCK_LENGTH,
MIN_REPLICATION_FACTOR, NORMAL_DISPATCH_RATIO, SHANNON, SLOT_PROBABILITY, SSC,
};

sp_runtime::impl_opaque_keys! {
Expand Down Expand Up @@ -146,10 +148,6 @@ pub const MILLISECS_PER_BLOCK: u64 = 6000;
// Attempting to do so will brick block production.
const SLOT_DURATION: u64 = 1000;

/// 1 in 6 slots (on average, not counting collisions) will have a block.
/// Must match ratio between block and slot duration in constants above.
const SLOT_PROBABILITY: (u64, u64) = (1, 6);

/// Number of slots between slot arrival and when corresponding block can be produced.
const BLOCK_AUTHORING_DELAY: SlotNumber = 4;

Expand Down Expand Up @@ -198,23 +196,13 @@ const RECENT_HISTORY_FRACTION: (HistorySize, HistorySize) = (
const MIN_SECTOR_LIFETIME: HistorySize =
HistorySize::new(NonZeroU64::new(4).expect("Not zero; qed"));

/// The block weight for 2 seconds of compute
const BLOCK_WEIGHT_FOR_2_SEC: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);

/// A ratio of `Normal` dispatch class within block, for `BlockWeight` and `BlockLength`.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);

/// Maximum block length for non-`Normal` extrinsic is 5 MiB.
const MAX_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;

parameter_types! {
pub const Version: RuntimeVersion = VERSION;
pub const BlockHashCount: BlockNumber = 250;
/// We allow for 2 seconds of compute with a 6 second average block time.
pub SubspaceBlockWeights: BlockWeights = BlockWeights::with_sensible_defaults(BLOCK_WEIGHT_FOR_2_SEC, NORMAL_DISPATCH_RATIO);
/// We allow for 3.75 MiB for `Normal` extrinsic with 5 MiB maximum block length.
pub SubspaceBlockLength: BlockLength = BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO);
pub SubspaceBlockLength: BlockLength = maximum_normal_block_length();
}

pub type SS58Prefix = ConstU16<2254>;
Expand Down Expand Up @@ -587,7 +575,7 @@ parameter_types! {
/// Use the consensus chain's `Normal` extrinsics block size limit as the domain block size limit
pub MaxDomainBlockSize: u32 = NORMAL_DISPATCH_RATIO * MAX_BLOCK_LENGTH;
/// Use the consensus chain's `Normal` extrinsics block weight limit as the domain block weight limit
pub MaxDomainBlockWeight: Weight = NORMAL_DISPATCH_RATIO * BLOCK_WEIGHT_FOR_2_SEC;
pub MaxDomainBlockWeight: Weight = maximum_domain_block_weight();
pub const MaxBundlesPerBlock: u32 = 10;
pub const DomainInstantiationDeposit: Balance = 100 * SSC;
pub const MaxDomainNameLength: u32 = 32;
Expand Down
88 changes: 64 additions & 24 deletions domains/primitives/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
pub use fp_account::AccountId20;
use frame_support::dispatch::{DispatchClass, PerDispatchClass};
use frame_support::dispatch::DispatchClass;
use frame_support::weights::constants::{BlockExecutionWeight, ExtrinsicBaseWeight};
use frame_system::limits::{BlockLength, BlockWeights};
use parity_scale_codec::{Decode, Encode};
Expand All @@ -35,8 +35,9 @@ use sp_runtime::generic::UncheckedExtrinsic;
use sp_runtime::traits::{Convert, IdentifyAccount, Verify};
use sp_runtime::transaction_validity::TransactionValidityError;
use sp_runtime::{MultiAddress, MultiSignature, Perbill};
use sp_weights::constants::WEIGHT_REF_TIME_PER_SECOND;
use sp_weights::Weight;
use subspace_runtime_primitives::SHANNON;
use subspace_runtime_primitives::{MAX_BLOCK_LENGTH, SHANNON, SLOT_PROBABILITY};

/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
Expand Down Expand Up @@ -66,27 +67,29 @@ pub const SLOT_DURATION: u64 = 1000;
/// The EVM chain Id type
pub type EVMChainId = u64;

/// Maximum block length for mandatory dispatch.
pub const MAXIMUM_MANDATORY_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;

/// Maximum block length for operational and normal dispatches.
pub const MAXIMUM_OPERATIONAL_AND_NORMAL_BLOCK_LENGTH: u32 = u32::MAX;
/// Dispatch ratio for domains
pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(65);

/// The maximum domain block weight with 3.25 MiB as proof size
/// Consensus allows 3.75 MiB but Fraud proof can carry extra size along with proof size
/// So we set the proof size to 3.25 MiB
pub fn maximum_domain_block_weight() -> Weight {
let consensus_maximum_normal_block_length =
*maximum_block_length().max.get(DispatchClass::Normal) as u64;
let weight =
NORMAL_DISPATCH_RATIO * Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), 0);
weight.set_proof_size(consensus_maximum_normal_block_length)
}

/// Custom error when nonce overflow occurs.
pub const ERR_NONCE_OVERFLOW: u8 = 100;
/// Custom error when balance overflow occurs.
pub const ERR_BALANCE_OVERFLOW: u8 = 200;

/// Maximum block length for all dispatches.
/// This is set to 3.75 MiB since consensus chain supports on 3.75 MiB for normal
pub fn maximum_block_length() -> BlockLength {
BlockLength {
max: PerDispatchClass::new(|class| match class {
DispatchClass::Normal | DispatchClass::Operational => {
MAXIMUM_OPERATIONAL_AND_NORMAL_BLOCK_LENGTH
}
DispatchClass::Mandatory => MAXIMUM_MANDATORY_BLOCK_LENGTH,
}),
}
BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO)
}

/// The existential deposit. Same with the one on primary chain.
Expand All @@ -96,22 +99,59 @@ pub const EXISTENTIAL_DEPOSIT: Balance = 500 * SHANNON;
/// used to limit the maximal weight of a single extrinsic.
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);

/// Maximum total block weight.
pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(u64::MAX, u64::MAX);
/// Calculates the max bundle weight
// See https://forum.subspace.network/t/on-bundle-weight-limits-sum/2277 for more details
// about the formula
pub fn calculate_max_bundle_weight(
max_domain_block_weight: Weight,
consensus_slot_probability: (u64, u64),
bundle_slot_probability: (u64, u64),
) -> Option<(u64, Weight)> {
// (n1 / d1) / (n2 / d2) is equal to (n1 * d2) / (d1 * n2)
// This represents: bundle_slot_probability/SLOT_PROBABILITY
let expected_bundles_per_block = bundle_slot_probability
.0
.checked_mul(consensus_slot_probability.1)?
.checked_div(
bundle_slot_probability
.1
.checked_mul(consensus_slot_probability.0)?,
)?;

// set the proof size for bundle to be proof size of max domain weight
// so that each domain extrinsic can use the full proof size if required
let max_proof_size = max_domain_block_weight.proof_size();
let max_bundle_weight = max_domain_block_weight.checked_div(expected_bundles_per_block)?;
Some((
expected_bundles_per_block,
max_bundle_weight.set_proof_size(max_proof_size),
))
}

/// Calculates the maximum extrinsic weight for domains.
/// We take bundle slot probability to be always at the maximum i.e 1 such that
/// operator can produce bundle in each slot
/// we also set the maximum extrinsic POV to be 3.75 MiB which is what Consensus allows
fn maximum_domain_extrinsic_weight() -> Option<Weight> {
let (_, max_bundle_weight) =
calculate_max_bundle_weight(maximum_domain_block_weight(), SLOT_PROBABILITY, (1, 1))?;
Some(max_bundle_weight)
}

pub fn block_weights() -> BlockWeights {
// allow u64::MAX for ref_time and proof size for total domain weight
let maximum_block_weight = Weight::from_parts(u64::MAX, u64::MAX);
let max_extrinsic_weight =
maximum_domain_extrinsic_weight().expect("Maximum extrinsic weight must always be valid");
BlockWeights::builder()
.base_block(BlockExecutionWeight::get())
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = ExtrinsicBaseWeight::get();
})
.for_class(DispatchClass::Normal, |weights| {
// maximum weight of each transaction would be the maximum weight of
// single bundle
weights.max_extrinsic = Some(max_extrinsic_weight);
// explicitly set max_total weight for normal dispatches to maximum
weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
})
.for_class(DispatchClass::Operational, |weights| {
// explicitly set max_total weight for operational dispatches to maximum
weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
weights.max_total = Some(maximum_block_weight);
})
.avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
.build_or_panic()
Expand Down
2 changes: 1 addition & 1 deletion domains/runtime/auto-id/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use codec::{Decode, Encode, MaxEncodedLen};
use domain_runtime_primitives::opaque::Header;
pub use domain_runtime_primitives::{
block_weights, maximum_block_length, opaque, Balance, BlockNumber, Hash, Nonce,
EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT,
EXISTENTIAL_DEPOSIT,
};
use domain_runtime_primitives::{
AccountId, Address, CheckExtrinsicsValidityError, DecodeExtrinsicError, Signature,
Expand Down
6 changes: 3 additions & 3 deletions domains/runtime/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use alloc::format;
use codec::{Decode, Encode, MaxEncodedLen};
use domain_runtime_primitives::opaque::Header;
pub use domain_runtime_primitives::{
block_weights, maximum_block_length, opaque, Balance, BlockNumber, Hash, Nonce,
EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT,
block_weights, maximum_block_length, maximum_domain_block_weight, opaque, Balance, BlockNumber,
Hash, Nonce, EXISTENTIAL_DEPOSIT,
};
use domain_runtime_primitives::{
CheckExtrinsicsValidityError, DecodeExtrinsicError, ERR_BALANCE_OVERFLOW, ERR_NONCE_OVERFLOW,
Expand Down Expand Up @@ -589,7 +589,7 @@ pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND.saturating_div(GAS_PE
parameter_types! {
/// EVM block gas limit is set to maximum to allow all the transaction stored on Consensus chain.
pub BlockGasLimit: U256 = U256::from(
MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS
maximum_domain_block_weight().ref_time() / WEIGHT_PER_GAS
);
pub PrecompilesValue: Precompiles = Precompiles::default();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
Expand Down
Loading

0 comments on commit 447299b

Please sign in to comment.