subspace_runtime_primitives/
lib.rs

1//! Runtime primitives for Subspace Network.
2#![cfg_attr(not(feature = "std"), no_std)]
3
4pub mod extension;
5pub mod utility;
6pub mod weights;
7
8#[cfg(not(feature = "std"))]
9extern crate alloc;
10
11use crate::time::{BLOCKS_IN_A_DAY, BLOCKS_IN_AN_MINUTE};
12#[cfg(not(feature = "std"))]
13use alloc::vec::Vec;
14use core::marker::PhantomData;
15use frame_support::dispatch::DispatchResult;
16use frame_support::pallet_prelude::Weight;
17use frame_support::traits::tokens;
18use frame_support::weights::WeightToFee;
19use frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND;
20use frame_support::{Deserialize, Serialize};
21use frame_system::limits::BlockLength;
22use frame_system::offchain::CreateTransactionBase;
23use frame_system::pallet_prelude::BlockNumberFor;
24use pallet_transaction_payment::{
25    Multiplier, NextFeeMultiplier, OnChargeTransaction, TargetedFeeAdjustment,
26};
27use parity_scale_codec::{Codec, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
28use scale_info::TypeInfo;
29use sp_core::parameter_types;
30use sp_runtime::traits::{Block as BlockT, Bounded, Header as HeaderT, IdentifyAccount, Verify};
31use sp_runtime::{FixedPointNumber, MultiSignature, Perbill, Perquintill};
32pub use subspace_core_primitives::BlockNumber;
33
34/// Minimum desired number of replicas of the blockchain to be stored by the network,
35/// impacts storage fees.
36pub const MIN_REPLICATION_FACTOR: u16 = 25;
37
38/// The smallest unit of the token is called Shannon.
39pub const SHANNON: Balance = 1;
40/// Subspace Credits have 18 decimal places.
41pub const DECIMAL_PLACES: u8 = 18;
42/// One Subspace Credit.
43pub const AI3: Balance = (10 * SHANNON).pow(DECIMAL_PLACES as u32);
44/// A ratio of `Normal` dispatch class within block, for `BlockWeight` and `BlockLength`.
45pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
46/// 1 in 6 slots (on average, not counting collisions) will have a block.
47/// Must match ratio between block and slot duration in constants above.
48pub const SLOT_PROBABILITY: (u64, u64) = (1, 6);
49/// The block weight for 2 seconds of compute
50pub const BLOCK_WEIGHT_FOR_2_SEC: Weight =
51    Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);
52
53/// Maximum block length for non-`Normal` extrinsic is 5 MiB.
54pub const MAX_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;
55
56/// Pruning depth multiplier for state and blocks pruning.
57pub const DOMAINS_PRUNING_DEPTH_MULTIPLIER: u32 = 2;
58
59/// We allow for 3.75 MiB for `Normal` extrinsic with 5 MiB maximum block length.
60pub fn maximum_normal_block_length() -> BlockLength {
61    BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO)
62}
63
64/// The maximum recursion depth we allow when parsing calls.
65/// This is a safety measure to avoid stack overflows.
66///
67/// Deeper nested calls can result in an error, or, if it is secure, the call is skipped.
68/// (Some code does unlimited heap-based recursion via `nested_utility_call_iter()`.)
69pub const MAX_CALL_RECURSION_DEPTH: u32 = 10;
70
71/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
72pub type Signature = MultiSignature;
73
74/// Some way of identifying an account on the chain. We intentionally make it equivalent
75/// to the public key of our transaction signing scheme.
76//
77// Note: sometimes this type alias causes complex trait ambiguity / conflicting implementation errors.
78// As a workaround, `use sp_runtime::AccountId32 as AccountId` instead.
79pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
80
81/// Balance of an account.
82pub type Balance = u128;
83
84/// Index of a transaction in the chain.
85pub type Nonce = u32;
86
87/// A hash of some data used by the chain.
88pub type Hash = sp_core::H256;
89
90/// Type used for expressing timestamp.
91pub type Moment = u64;
92
93/// Type alias for extrinsics.
94pub type ExtrinsicFor<Block> = <Block as BlockT>::Extrinsic;
95
96/// Type alias for block hash.
97pub type BlockHashFor<Block> = <Block as BlockT>::Hash;
98
99/// Type alias for block header.
100pub type HeaderFor<Block> = <Block as BlockT>::Header;
101
102/// Type alias for block hashing.
103pub type BlockHashingFor<Block> = <HeaderFor<Block> as HeaderT>::Hashing;
104
105parameter_types! {
106    /// Event segments are disabled on the consensus chain.
107    pub const ConsensusEventSegmentSize: u32 = 0;
108    /// Event segments are enabled on domain chains, this value was derived from benchmarking.
109    pub const DomainEventSegmentSize: u32 = 100;
110}
111
112/// Opaque types.
113///
114/// These are used by the CLI to instantiate machinery that don't need to know the specifics of the
115/// runtime. They can then be made to be agnostic over specific formats of data like extrinsics,
116/// allowing for them to continue syncing the network through upgrades to even the core data
117/// structures.
118pub mod opaque {
119    use super::BlockNumber;
120    pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
121    use sp_runtime::generic;
122    use sp_runtime::traits::BlakeTwo256;
123
124    /// Opaque block header type.
125    pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
126    /// Opaque block type.
127    pub type Block = generic::Block<Header, UncheckedExtrinsic>;
128}
129
130pub mod time {
131    /// Expected block time in milliseconds.
132    ///
133    /// Since Subspace is probabilistic this is the average expected block time that
134    /// we are targeting. Blocks will be produced at a minimum duration defined
135    /// by `SLOT_DURATION`, but some slots will not be allocated to any
136    /// farmer and hence no block will be produced. We expect to have this
137    /// block time on average following the defined slot duration and the value
138    /// of `c` configured for Subspace (where `1 - c` represents the probability of
139    /// a slot being empty).
140    /// This value is only used indirectly to define the unit constants below
141    /// that are expressed in blocks. The rest of the code should use
142    /// `SLOT_DURATION` instead (like the Timestamp pallet for calculating the
143    /// minimum period).
144    ///
145    /// Based on:
146    /// <https://research.web3.foundation/en/latest/polkadot/block-production/Babe.html#-6.-practical-results>
147    pub const MILLISECS_PER_BLOCK: u64 = 6000;
148    /// Approximate number of block in a minute.
149    pub const BLOCKS_IN_AN_MINUTE: u32 = (60 * 1000) / MILLISECS_PER_BLOCK as u32;
150    /// Approximate number of blocks in an hour.
151    pub const BLOCKS_IN_AN_HOUR: u32 = 60 * BLOCKS_IN_AN_MINUTE;
152    /// Approximate number of blocks in a day.
153    pub const BLOCKS_IN_A_DAY: u32 = 24 * BLOCKS_IN_AN_HOUR;
154}
155
156#[derive(Copy, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, MaxEncodedLen, Debug)]
157pub struct CouncilDemocracyConfigParams<BlockNumber> {
158    /// Council motion duration.
159    pub council_motion_duration: BlockNumber,
160    /// Democracy cooloff period.
161    pub democracy_cooloff_period: BlockNumber,
162    /// Democracy enactment period.
163    pub democracy_enactment_period: BlockNumber,
164    /// Fast track voting period.
165    pub democracy_fast_track_voting_period: BlockNumber,
166    /// Launch period.
167    pub democracy_launch_period: BlockNumber,
168    /// Vote locking period.
169    pub democracy_vote_locking_period: BlockNumber,
170    /// Voting period.
171    pub democracy_voting_period: BlockNumber,
172}
173
174impl<BlockNumber: From<u32>> Default for CouncilDemocracyConfigParams<BlockNumber> {
175    fn default() -> Self {
176        Self {
177            council_motion_duration: BLOCKS_IN_A_DAY.into(),
178            democracy_cooloff_period: BLOCKS_IN_A_DAY.into(),
179            democracy_enactment_period: BLOCKS_IN_A_DAY.into(),
180            democracy_fast_track_voting_period: (2 * BLOCKS_IN_A_DAY).into(),
181            democracy_launch_period: (2 * BLOCKS_IN_A_DAY).into(),
182            democracy_vote_locking_period: BLOCKS_IN_A_DAY.into(),
183            democracy_voting_period: BLOCKS_IN_A_DAY.into(),
184        }
185    }
186}
187
188impl<BlockNumber: From<u32>> CouncilDemocracyConfigParams<BlockNumber> {
189    /// Production params for Council democracy config.
190    pub fn production_params() -> Self {
191        Self::default()
192    }
193
194    /// Fast period params for Council democracy config.
195    pub fn fast_params() -> Self {
196        Self {
197            council_motion_duration: (15 * BLOCKS_IN_AN_MINUTE).into(),
198            democracy_cooloff_period: (5 * BLOCKS_IN_AN_MINUTE).into(),
199            democracy_enactment_period: (15 * BLOCKS_IN_AN_MINUTE).into(),
200            democracy_fast_track_voting_period: (5 * BLOCKS_IN_AN_MINUTE).into(),
201            democracy_launch_period: (15 * BLOCKS_IN_AN_MINUTE).into(),
202            democracy_vote_locking_period: BLOCKS_IN_AN_MINUTE.into(),
203            democracy_voting_period: (15 * BLOCKS_IN_AN_MINUTE).into(),
204        }
205    }
206}
207/// Config parameters for genesis.
208pub struct GenesisConfigParams {
209    /// Confirmation depth K
210    pub confirmation_depth_k: BlockNumber,
211    /// Domain pruning depth.
212    pub domain_block_pruning_depth: BlockNumber,
213    /// Staking withdrawal period.
214    pub staking_withdrawal_period: BlockNumber,
215}
216
217impl GenesisConfigParams {
218    /// Production specific domain parameters.
219    pub const fn production_params() -> Self {
220        Self {
221            confirmation_depth_k: 100u32,
222            domain_block_pruning_depth: 14_400u32,
223            staking_withdrawal_period: 14_400u32,
224        }
225    }
226
227    /// Development specific domain parameters.
228    pub const fn dev_params() -> Self {
229        Self {
230            confirmation_depth_k: 5u32,
231            domain_block_pruning_depth: 5u32,
232            staking_withdrawal_period: 5u32,
233        }
234    }
235}
236
237/// A trait for determining whether rewards are enabled or not
238pub trait RewardsEnabled {
239    /// Determine whether rewards are enabled or not
240    fn rewards_enabled() -> bool;
241}
242
243/// A trait for finding the address for a block reward based on the `PreRuntime` digests contained within it.
244pub trait FindBlockRewardAddress<RewardAddress> {
245    /// Find the address for a block rewards based on the pre-runtime digests.
246    fn find_block_reward_address() -> Option<RewardAddress>;
247}
248
249/// A trait for finding the addresses for voting reward based on transactions found in the block.
250pub trait FindVotingRewardAddresses<RewardAddress> {
251    /// Find the addresses for voting rewards based on transactions found in the block.
252    fn find_voting_reward_addresses() -> Vec<RewardAddress>;
253}
254
255pub trait StorageFee<Balance> {
256    /// Return the consensus transaction byte fee.
257    fn transaction_byte_fee() -> Balance;
258
259    /// Note the charged storage fee.
260    fn note_storage_fees(fee: Balance);
261}
262
263parameter_types! {
264    /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less
265    /// than this will decrease the weight and more will increase.
266    pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(50);
267    /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
268    /// change the fees more rapidly.
269    pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(75, 1_000_000);
270    /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure
271    /// that combined with `AdjustmentVariable`, we can recover from the minimum.
272    /// See `multiplier_can_grow_from_zero`.
273    pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 10u128);
274    /// The maximum amount of the multiplier.
275    pub MaximumMultiplier: Multiplier = Bounded::max_value();
276}
277
278/// Parameterized slow adjusting fee updated based on
279/// <https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism>
280pub type SlowAdjustingFeeUpdate<R, TargetBlockFullness> = TargetedFeeAdjustment<
281    R,
282    TargetBlockFullness,
283    AdjustmentVariable,
284    MinimumMultiplier,
285    MaximumMultiplier,
286>;
287
288#[derive(Encode, Decode, TypeInfo)]
289pub struct BlockTransactionByteFee<Balance: Codec> {
290    // The value of `transaction_byte_fee` for the current block
291    pub current: Balance,
292    // The value of `transaction_byte_fee` for the next block
293    pub next: Balance,
294}
295
296impl<Balance: Codec + tokens::Balance> Default for BlockTransactionByteFee<Balance> {
297    fn default() -> Self {
298        BlockTransactionByteFee {
299            current: Balance::max_value(),
300            next: Balance::max_value(),
301        }
302    }
303}
304
305parameter_types! {
306    pub const XdmFeeMultipler: u32 = 5;
307}
308
309/// Balance type pointing to the OnChargeTransaction trait.
310pub type OnChargeTransactionBalance<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<
311    T,
312>>::Balance;
313
314/// Adjusted XDM Weight to fee Conversion.
315pub struct XdmAdjustedWeightToFee<T>(PhantomData<T>);
316impl<T: pallet_transaction_payment::Config> WeightToFee for XdmAdjustedWeightToFee<T> {
317    type Balance = OnChargeTransactionBalance<T>;
318
319    fn weight_to_fee(weight: &Weight) -> Self::Balance {
320        // the adjustable part of the fee.
321        let unadjusted_weight_fee = pallet_transaction_payment::Pallet::<T>::weight_to_fee(*weight);
322        let multiplier = NextFeeMultiplier::<T>::get();
323        // final adjusted weight fee.
324        multiplier.saturating_mul_int(unadjusted_weight_fee)
325    }
326}
327
328#[derive(
329    PartialEq,
330    Eq,
331    Clone,
332    Encode,
333    Decode,
334    TypeInfo,
335    MaxEncodedLen,
336    Ord,
337    PartialOrd,
338    Copy,
339    Debug,
340    DecodeWithMemTracking,
341)]
342pub enum HoldIdentifier {
343    DomainStaking,
344    DomainInstantiation,
345    DomainStorageFund,
346    MessengerChannel,
347    Preimage,
348}
349
350/// Interface for creating an unsigned general extrinsic
351pub trait CreateUnsigned<LocalCall>: CreateTransactionBase<LocalCall> {
352    /// Create an unsigned extrinsic.
353    fn create_unsigned(call: Self::RuntimeCall) -> Self::Extrinsic;
354}
355
356/// Callback to do something before setting code.
357pub trait OnSetCode<Number> {
358    /// Do something before setting code at the block number.
359    fn set_code(block_number: Number) -> DispatchResult;
360}
361
362/// Implements [frame_system::SetCode]
363pub struct SetCode<Runtime, OSC>(PhantomData<(Runtime, OSC)>);
364impl<Runtime, OSC> frame_system::SetCode<Runtime> for SetCode<Runtime, OSC>
365where
366    Runtime: frame_system::Config,
367    OSC: OnSetCode<BlockNumberFor<Runtime>>,
368{
369    fn set_code(code: Vec<u8>) -> DispatchResult {
370        let current_block_number = frame_system::Pallet::<Runtime>::block_number();
371        OSC::set_code(current_block_number)?;
372        frame_system::Pallet::<Runtime>::update_code_in_storage(&code);
373        Ok(())
374    }
375}
376
377#[cfg(feature = "testing")]
378pub mod tests_utils {
379    use frame_support::dispatch::DispatchClass;
380    use frame_support::weights::Weight;
381    use frame_system::limits::BlockWeights;
382    use pallet_transaction_payment::{Multiplier, MultiplierUpdate};
383    use sp_runtime::BuildStorage;
384    use sp_runtime::traits::{Convert, Get};
385    use std::marker::PhantomData;
386
387    pub struct FeeMultiplierUtils<Runtime, BlockWeightsGetter>(
388        PhantomData<(Runtime, BlockWeightsGetter)>,
389    );
390
391    impl<Runtime, BlockWeightsGetter> FeeMultiplierUtils<Runtime, BlockWeightsGetter>
392    where
393        Runtime: frame_system::Config + pallet_transaction_payment::Config,
394        BlockWeightsGetter: Get<BlockWeights>,
395    {
396        fn max_normal() -> Weight {
397            let block_weights = BlockWeightsGetter::get();
398            block_weights
399                .get(DispatchClass::Normal)
400                .max_total
401                .unwrap_or(block_weights.max_block)
402        }
403
404        fn min_multiplier() -> Multiplier {
405            <Runtime as pallet_transaction_payment::Config>::FeeMultiplierUpdate::min()
406        }
407
408        fn target() -> Weight {
409            <Runtime as pallet_transaction_payment::Config>::FeeMultiplierUpdate::target()
410                * Self::max_normal()
411        }
412
413        // update based on runtime impl.
414        fn runtime_multiplier_update(fm: Multiplier) -> Multiplier {
415            <Runtime as pallet_transaction_payment::Config>::FeeMultiplierUpdate::convert(fm)
416        }
417
418        fn run_with_system_weight<F>(w: Weight, assertions: F)
419        where
420            F: Fn(),
421        {
422            let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
423                .build_storage()
424                .unwrap()
425                .into();
426            t.execute_with(|| {
427                frame_system::Pallet::<Runtime>::set_block_consumed_resources(w, 0);
428                assertions()
429            });
430        }
431
432        // The following function is taken from test with same name from
433        // https://github.com/paritytech/polkadot-sdk/blob/91851951856b8effe627fb1d151fe336a51eef2d/substrate/bin/node/runtime/src/impls.rs#L234
434        // with some small surface changes.
435        pub fn multiplier_can_grow_from_zero()
436        where
437            Runtime: pallet_transaction_payment::Config,
438            BlockWeightsGetter: Get<BlockWeights>,
439        {
440            // if the min is too small, then this will not change, and we are doomed forever.
441            // the block ref time is 1/100th bigger than target.
442            Self::run_with_system_weight(
443                Self::target().set_ref_time((Self::target().ref_time() / 100) * 101),
444                || {
445                    let next = Self::runtime_multiplier_update(Self::min_multiplier());
446                    assert!(
447                        next > Self::min_multiplier(),
448                        "{:?} !> {:?}",
449                        next,
450                        Self::min_multiplier()
451                    );
452                },
453            );
454
455            // the block proof size is 1/100th bigger than target.
456            Self::run_with_system_weight(
457                Self::target().set_proof_size((Self::target().proof_size() / 100) * 101),
458                || {
459                    let next = Self::runtime_multiplier_update(Self::min_multiplier());
460                    assert!(
461                        next > Self::min_multiplier(),
462                        "{:?} !> {:?}",
463                        next,
464                        Self::min_multiplier()
465                    );
466                },
467            )
468        }
469    }
470}