sp_domains/
lib.rs

1//! Primitives for domains pallet.
2
3#![cfg_attr(not(feature = "std"), no_std)]
4
5pub mod bundle;
6pub mod bundle_producer_election;
7pub mod core_api;
8pub mod execution_receipt;
9pub mod extrinsics;
10pub mod merkle_tree;
11pub mod proof_provider_and_verifier;
12pub mod storage;
13#[cfg(test)]
14mod tests;
15pub mod valued_trie;
16
17#[cfg(not(feature = "std"))]
18extern crate alloc;
19
20use crate::bundle::{BundleVersion, OpaqueBundle, OpaqueBundles};
21use crate::execution_receipt::ExecutionReceiptVersion;
22use crate::storage::{RawGenesis, StorageKey};
23#[cfg(not(feature = "std"))]
24use alloc::collections::BTreeSet;
25#[cfg(not(feature = "std"))]
26use alloc::string::String;
27#[cfg(not(feature = "std"))]
28use alloc::vec::Vec;
29use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionError};
30use core::num::ParseIntError;
31use core::ops::{Add, Sub};
32use core::str::FromStr;
33use domain_runtime_primitives::{EthereumAccountId, MultiAccountId};
34use execution_receipt::{ExecutionReceiptFor, SealedSingletonReceipt};
35use frame_support::storage::storage_prefix;
36use frame_support::{Blake2_128Concat, StorageHasher};
37use hex_literal::hex;
38use parity_scale_codec::{Codec, Decode, Encode, MaxEncodedLen};
39use scale_info::TypeInfo;
40use serde::{Deserialize, Serialize};
41use sp_core::H256;
42use sp_core::crypto::KeyTypeId;
43use sp_core::sr25519::vrf::VrfSignature;
44#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
45use sp_core::sr25519::vrf::{VrfPreOutput, VrfProof};
46use sp_runtime::generic::OpaqueDigestItemId;
47use sp_runtime::traits::{CheckedAdd, Hash as HashT, Header as HeaderT, NumberFor};
48use sp_runtime::{Digest, DigestItem, Percent};
49use sp_runtime_interface::pass_by;
50use sp_runtime_interface::pass_by::PassBy;
51use sp_std::collections::btree_map::BTreeMap;
52use sp_std::fmt::{Display, Formatter};
53use sp_trie::TrieLayout;
54use sp_version::RuntimeVersion;
55use sp_weights::Weight;
56#[cfg(feature = "std")]
57use std::collections::BTreeSet;
58use subspace_core_primitives::hashes::{Blake3Hash, blake3_hash};
59use subspace_core_primitives::pot::PotOutput;
60use subspace_core_primitives::solutions::bidirectional_distance;
61use subspace_core_primitives::{Randomness, U256};
62use subspace_runtime_primitives::{Balance, Moment};
63
64/// Key type for Operator.
65pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"oper");
66
67/// Extrinsics shuffling seed
68pub const DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT: &[u8] = b"extrinsics-shuffling-seed";
69
70mod app {
71    use super::KEY_TYPE;
72    use sp_application_crypto::{app_crypto, sr25519};
73
74    app_crypto!(sr25519, KEY_TYPE);
75}
76
77// TODO: this runtime constant is not support to update, see https://github.com/autonomys/subspace/issues/2712
78// for more detail about the problem and what we need to do to support it.
79//
80// The domain storage fee multiplier used to charge a higher storage fee to the domain
81// transaction to even out the duplicated/illegal domain transaction storage cost, which
82// can not be eliminated right now.
83pub const DOMAIN_STORAGE_FEE_MULTIPLIER: Balance = 3;
84
85/// An operator authority signature.
86pub type OperatorSignature = app::Signature;
87
88/// An operator authority keypair. Necessarily equivalent to the schnorrkel public key used in
89/// the main executor module. If that ever changes, then this must, too.
90#[cfg(feature = "std")]
91pub type OperatorPair = app::Pair;
92
93/// An operator authority identifier.
94pub type OperatorPublicKey = app::Public;
95
96/// A type that implements `BoundToRuntimeAppPublic`, used for operator signing key.
97pub struct OperatorKey;
98
99impl sp_runtime::BoundToRuntimeAppPublic for OperatorKey {
100    type Public = OperatorPublicKey;
101}
102
103/// Stake weight in the domain bundle election.
104///
105/// Derived from the Balance and can't be smaller than u128.
106pub type StakeWeight = u128;
107
108/// The Trie root of all extrinsics included in a bundle.
109pub type ExtrinsicsRoot = H256;
110
111/// Type alias for Header Hashing.
112pub type HeaderHashingFor<Header> = <Header as HeaderT>::Hashing;
113/// Type alias for Header number.
114pub type HeaderNumberFor<Header> = <Header as HeaderT>::Number;
115/// Type alias for Header hash.
116pub type HeaderHashFor<Header> = <Header as HeaderT>::Hash;
117
118/// Unique identifier of a domain.
119#[derive(
120    Clone,
121    Copy,
122    Debug,
123    Hash,
124    Default,
125    Eq,
126    PartialEq,
127    Ord,
128    PartialOrd,
129    Encode,
130    Decode,
131    TypeInfo,
132    Serialize,
133    Deserialize,
134    MaxEncodedLen,
135)]
136pub struct DomainId(u32);
137
138impl From<u32> for DomainId {
139    #[inline]
140    fn from(x: u32) -> Self {
141        Self(x)
142    }
143}
144
145impl From<DomainId> for u32 {
146    #[inline]
147    fn from(domain_id: DomainId) -> Self {
148        domain_id.0
149    }
150}
151
152impl FromStr for DomainId {
153    type Err = ParseIntError;
154
155    fn from_str(s: &str) -> Result<Self, Self::Err> {
156        s.parse::<u32>().map(Into::into)
157    }
158}
159
160impl Add<DomainId> for DomainId {
161    type Output = Self;
162
163    fn add(self, other: DomainId) -> Self {
164        Self(self.0 + other.0)
165    }
166}
167
168impl Sub<DomainId> for DomainId {
169    type Output = Self;
170
171    fn sub(self, other: DomainId) -> Self {
172        Self(self.0 - other.0)
173    }
174}
175
176impl CheckedAdd for DomainId {
177    fn checked_add(&self, rhs: &Self) -> Option<Self> {
178        self.0.checked_add(rhs.0).map(Self)
179    }
180}
181
182impl DomainId {
183    /// Creates a [`DomainId`].
184    pub const fn new(id: u32) -> Self {
185        Self(id)
186    }
187
188    /// Converts the inner integer to little-endian bytes.
189    pub fn to_le_bytes(&self) -> [u8; 4] {
190        self.0.to_le_bytes()
191    }
192}
193
194impl Display for DomainId {
195    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
196        self.0.fmt(f)
197    }
198}
199
200impl PassBy for DomainId {
201    type PassBy = pass_by::Codec<Self>;
202}
203
204/// Identifier of a chain.
205#[derive(
206    Clone,
207    Copy,
208    Debug,
209    Hash,
210    Eq,
211    PartialEq,
212    Ord,
213    PartialOrd,
214    Encode,
215    Decode,
216    TypeInfo,
217    Serialize,
218    Deserialize,
219    MaxEncodedLen,
220)]
221pub enum ChainId {
222    Consensus,
223    Domain(DomainId),
224}
225
226impl ChainId {
227    #[inline]
228    pub fn consensus_chain_id() -> Self {
229        Self::Consensus
230    }
231
232    #[inline]
233    pub fn is_consensus_chain(&self) -> bool {
234        match self {
235            ChainId::Consensus => true,
236            ChainId::Domain(_) => false,
237        }
238    }
239
240    #[inline]
241    pub fn maybe_domain_chain(&self) -> Option<DomainId> {
242        match self {
243            ChainId::Consensus => None,
244            ChainId::Domain(domain_id) => Some(*domain_id),
245        }
246    }
247}
248
249impl From<u32> for ChainId {
250    #[inline]
251    fn from(x: u32) -> Self {
252        Self::Domain(DomainId::new(x))
253    }
254}
255
256impl From<DomainId> for ChainId {
257    #[inline]
258    fn from(x: DomainId) -> Self {
259        Self::Domain(x)
260    }
261}
262
263// TODO: this runtime constant is not support to update, see https://github.com/autonomys/subspace/issues/2712
264// for more detail about the problem and what we need to do to support it.
265//
266/// Initial tx range = U256::MAX / INITIAL_DOMAIN_TX_RANGE.
267pub const INITIAL_DOMAIN_TX_RANGE: u64 = 3;
268
269#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
270pub struct ProofOfElection {
271    /// Domain id.
272    pub domain_id: DomainId,
273    /// The slot number.
274    pub slot_number: u64,
275    /// The PoT output for `slot_number`.
276    pub proof_of_time: PotOutput,
277    /// VRF signature.
278    pub vrf_signature: VrfSignature,
279    /// Operator index in the OperatorRegistry.
280    pub operator_id: OperatorId,
281}
282
283impl ProofOfElection {
284    pub fn verify_vrf_signature(
285        &self,
286        operator_signing_key: &OperatorPublicKey,
287    ) -> Result<(), ProofOfElectionError> {
288        let global_challenge = self
289            .proof_of_time
290            .derive_global_randomness()
291            .derive_global_challenge(self.slot_number);
292        bundle_producer_election::verify_vrf_signature(
293            self.domain_id,
294            operator_signing_key,
295            &self.vrf_signature,
296            &global_challenge,
297        )
298    }
299
300    /// Computes the VRF hash.
301    pub fn vrf_hash(&self) -> Blake3Hash {
302        let mut bytes = self.vrf_signature.pre_output.encode();
303        bytes.append(&mut self.vrf_signature.proof.encode());
304        blake3_hash(&bytes)
305    }
306
307    pub fn slot_number(&self) -> u64 {
308        self.slot_number
309    }
310}
311
312impl ProofOfElection {
313    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
314    pub fn dummy(domain_id: DomainId, operator_id: OperatorId) -> Self {
315        let output_bytes = sp_std::vec![0u8; VrfPreOutput::max_encoded_len()];
316        let proof_bytes = sp_std::vec![0u8; VrfProof::max_encoded_len()];
317        let vrf_signature = VrfSignature {
318            pre_output: VrfPreOutput::decode(&mut output_bytes.as_slice()).unwrap(),
319            proof: VrfProof::decode(&mut proof_bytes.as_slice()).unwrap(),
320        };
321        Self {
322            domain_id,
323            slot_number: 0u64,
324            proof_of_time: PotOutput::default(),
325            vrf_signature,
326            operator_id,
327        }
328    }
329}
330
331/// Type that represents an operator allow list for Domains.
332#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
333pub enum OperatorAllowList<AccountId: Ord> {
334    /// Anyone can operate for this domain.
335    Anyone,
336    /// Only the specific operators are allowed to operate the domain.
337    /// This essentially makes the domain permissioned.
338    Operators(BTreeSet<AccountId>),
339}
340
341impl<AccountId: Ord> OperatorAllowList<AccountId> {
342    /// Returns true if the allow list is either `Anyone` or the operator is part of the allowed operator list.
343    pub fn is_operator_allowed(&self, operator: &AccountId) -> bool {
344        match self {
345            OperatorAllowList::Anyone => true,
346            OperatorAllowList::Operators(allowed_operators) => allowed_operators.contains(operator),
347        }
348    }
349}
350
351/// Permissioned actions allowed by either specific accounts or anyone.
352#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
353pub enum PermissionedActionAllowedBy<AccountId: Codec + Clone> {
354    Accounts(Vec<AccountId>),
355    Anyone,
356}
357
358impl<AccountId: Codec + PartialEq + Clone> PermissionedActionAllowedBy<AccountId> {
359    pub fn is_allowed(&self, who: &AccountId) -> bool {
360        match self {
361            PermissionedActionAllowedBy::Accounts(accounts) => accounts.contains(who),
362            PermissionedActionAllowedBy::Anyone => true,
363        }
364    }
365
366    pub fn is_anyone_allowed(&self) -> bool {
367        matches!(self, PermissionedActionAllowedBy::Anyone)
368    }
369}
370
371/// EVM-specific domain type (and associated data).
372#[derive(
373    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
374)]
375pub enum EvmType {
376    #[default]
377    /// An EVM domain where any account can create contracts.
378    Public,
379    /// An EVM domain with a contract creation allow list.
380    Private {
381        /// Accounts initially allowed to create contracts on a private EVM domain.
382        /// The domain owner can update this list using a pallet-domains call (or there's a sudo call).
383        initial_contract_creation_allow_list: PermissionedActionAllowedBy<EthereumAccountId>,
384    },
385}
386
387impl EvmType {
388    /// Returns the initial contract creation allow list, or `None` if this is a public EVM domain.
389    pub fn initial_contract_creation_allow_list(
390        &self,
391    ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
392        match self {
393            EvmType::Public => None,
394            EvmType::Private {
395                initial_contract_creation_allow_list,
396            } => Some(initial_contract_creation_allow_list),
397        }
398    }
399
400    /// Returns true if the EVM domain is public.
401    pub fn is_public_evm_domain(&self) -> bool {
402        matches!(self, EvmType::Public)
403    }
404
405    /// Returns true if the EVM domain is private.
406    pub fn is_private_evm_domain(&self) -> bool {
407        matches!(self, EvmType::Private { .. })
408    }
409}
410
411/// EVM-specific domain runtime config.
412#[derive(
413    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
414)]
415pub struct EvmDomainRuntimeConfig {
416    pub evm_type: EvmType,
417}
418
419/// AutoId-specific domain runtime config.
420#[derive(
421    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
422)]
423pub struct AutoIdDomainRuntimeConfig {
424    // Currently, there is no specific configuration for AutoId.
425}
426
427/// Configurations for specific domain runtime kinds.
428#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
429pub enum DomainRuntimeConfig {
430    Evm(EvmDomainRuntimeConfig),
431    AutoId(AutoIdDomainRuntimeConfig),
432}
433
434impl Default for DomainRuntimeConfig {
435    fn default() -> Self {
436        Self::default_evm()
437    }
438}
439
440impl From<EvmDomainRuntimeConfig> for DomainRuntimeConfig {
441    fn from(evm_config: EvmDomainRuntimeConfig) -> Self {
442        DomainRuntimeConfig::Evm(evm_config)
443    }
444}
445
446impl From<AutoIdDomainRuntimeConfig> for DomainRuntimeConfig {
447    fn from(auto_id_config: AutoIdDomainRuntimeConfig) -> Self {
448        DomainRuntimeConfig::AutoId(auto_id_config)
449    }
450}
451
452impl DomainRuntimeConfig {
453    pub fn default_evm() -> Self {
454        DomainRuntimeConfig::Evm(EvmDomainRuntimeConfig::default())
455    }
456
457    pub fn default_auto_id() -> Self {
458        DomainRuntimeConfig::AutoId(AutoIdDomainRuntimeConfig::default())
459    }
460
461    pub fn is_evm_domain(&self) -> bool {
462        matches!(self, DomainRuntimeConfig::Evm(_))
463    }
464
465    pub fn is_auto_id(&self) -> bool {
466        matches!(self, DomainRuntimeConfig::AutoId(_))
467    }
468
469    pub fn evm(&self) -> Option<&EvmDomainRuntimeConfig> {
470        match self {
471            DomainRuntimeConfig::Evm(evm_config) => Some(evm_config),
472            _ => None,
473        }
474    }
475
476    pub fn initial_contract_creation_allow_list(
477        &self,
478    ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
479        self.evm()
480            .and_then(|evm_config| evm_config.evm_type.initial_contract_creation_allow_list())
481    }
482
483    pub fn auto_id(&self) -> Option<&AutoIdDomainRuntimeConfig> {
484        match self {
485            DomainRuntimeConfig::AutoId(auto_id_config) => Some(auto_id_config),
486            _ => None,
487        }
488    }
489}
490
491#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
492pub struct GenesisDomain<AccountId: Ord, Balance> {
493    // Domain runtime items
494    pub runtime_name: String,
495    pub runtime_type: RuntimeType,
496    pub runtime_version: RuntimeVersion,
497    pub raw_genesis_storage: Vec<u8>,
498
499    // Domain config items
500    pub owner_account_id: AccountId,
501    pub domain_name: String,
502    pub bundle_slot_probability: (u64, u64),
503    pub operator_allow_list: OperatorAllowList<AccountId>,
504    /// Configurations for a specific type of domain runtime, for example, EVM.
505    pub domain_runtime_config: DomainRuntimeConfig,
506
507    // Genesis operator
508    pub signing_key: OperatorPublicKey,
509    pub minimum_nominator_stake: Balance,
510    pub nomination_tax: Percent,
511
512    // initial balances
513    pub initial_balances: Vec<(MultiAccountId, Balance)>,
514}
515
516/// Types of runtime pallet domains currently supports
517#[derive(
518    Debug, Default, Encode, Decode, TypeInfo, Copy, Clone, PartialEq, Eq, Serialize, Deserialize,
519)]
520pub enum RuntimeType {
521    #[default]
522    Evm,
523    AutoId,
524}
525
526/// Type representing the runtime ID.
527pub type RuntimeId = u32;
528
529/// Type representing domain epoch.
530pub type EpochIndex = u32;
531
532/// Type representing operator ID
533pub type OperatorId = u64;
534
535/// Channel identity.
536pub type ChannelId = sp_core::U256;
537
538/// Domains specific digest item.
539#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
540pub enum DomainDigestItem {
541    DomainRuntimeUpgraded(RuntimeId),
542    DomainInstantiated(DomainId),
543}
544
545/// Domains specific digest items.
546pub trait DomainsDigestItem {
547    fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self;
548    fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId>;
549
550    fn domain_instantiation(domain_id: DomainId) -> Self;
551    fn as_domain_instantiation(&self) -> Option<DomainId>;
552}
553
554impl DomainsDigestItem for DigestItem {
555    fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self {
556        Self::Other(DomainDigestItem::DomainRuntimeUpgraded(runtime_id).encode())
557    }
558
559    fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId> {
560        match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
561            Some(DomainDigestItem::DomainRuntimeUpgraded(runtime_id)) => Some(runtime_id),
562            _ => None,
563        }
564    }
565
566    fn domain_instantiation(domain_id: DomainId) -> Self {
567        Self::Other(DomainDigestItem::DomainInstantiated(domain_id).encode())
568    }
569
570    fn as_domain_instantiation(&self) -> Option<DomainId> {
571        match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
572            Some(DomainDigestItem::DomainInstantiated(domain_id)) => Some(domain_id),
573            _ => None,
574        }
575    }
576}
577
578/// EVM chain Id storage key.
579///
580/// This function should ideally use a Host function to fetch the storage key
581/// from the domain runtime. But since the Host function is not available at Genesis, we have to
582/// assume the storage keys.
583/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
584pub(crate) fn evm_chain_id_storage_key() -> StorageKey {
585    StorageKey(
586        storage_prefix(
587            // This is the name used for `pallet_evm_chain_id` in the `construct_runtime` macro
588            // i.e. `EVMChainId: pallet_evm_chain_id = 82,`
589            "EVMChainId".as_bytes(),
590            // This is the storage item name used inside `pallet_evm_chain_id`
591            "ChainId".as_bytes(),
592        )
593        .to_vec(),
594    )
595}
596
597/// EVM contract creation allow list storage key.
598///
599/// This function should ideally use a Host function to fetch the storage key
600/// from the domain runtime. But since the Host function is not available at Genesis, we have to
601/// assume the storage keys.
602/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
603pub(crate) fn evm_contract_creation_allowed_by_storage_key() -> StorageKey {
604    StorageKey(
605        storage_prefix(
606            // This is the name used for `pallet_evm_tracker` in the `construct_runtime` macro
607            // i.e. `EVMNoncetracker: pallet_evm_tracker = 84,`
608            "EVMNoncetracker".as_bytes(),
609            // This is the storage item name used inside `pallet_evm_tracker`
610            "ContractCreationAllowedBy".as_bytes(),
611        )
612        .to_vec(),
613    )
614}
615
616/// Total issuance storage key for Domains.
617///
618/// This function should ideally use a Host function to fetch the storage key
619/// from the domain runtime. But since the Host function is not available at Genesis, we have to
620/// assume the storage keys.
621/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
622pub fn domain_total_issuance_storage_key() -> StorageKey {
623    StorageKey(
624        storage_prefix(
625            // This is the name used for `pallet_balances` in the `construct_runtime` macro
626            "Balances".as_bytes(),
627            // This is the storage item name used inside `pallet_balances`
628            "TotalIssuance".as_bytes(),
629        )
630        .to_vec(),
631    )
632}
633
634/// Account info on frame_system on Domains
635///
636/// This function should ideally use a Host function to fetch the storage key
637/// from the domain runtime. But since the Host function is not available at Genesis, we have to
638/// assume the storage keys.
639/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
640pub fn domain_account_storage_key<AccountId: Encode>(who: AccountId) -> StorageKey {
641    let storage_prefix = storage_prefix("System".as_bytes(), "Account".as_bytes());
642    let key_hashed = who.using_encoded(Blake2_128Concat::hash);
643
644    let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
645
646    final_key.extend_from_slice(&storage_prefix);
647    final_key.extend_from_slice(key_hashed.as_ref());
648
649    StorageKey(final_key)
650}
651
652/// The storage key of the `SelfDomainId` storage item in `pallet-domain-id`
653///
654/// Any change to the storage item name or `pallet-domain-id` name used in the `construct_runtime`
655/// macro must be reflected here.
656pub fn self_domain_id_storage_key() -> StorageKey {
657    StorageKey(
658        frame_support::storage::storage_prefix(
659            // This is the name used for `pallet-domain-id` in the `construct_runtime` macro
660            // i.e. `SelfDomainId: pallet_domain_id = 90`
661            "SelfDomainId".as_bytes(),
662            // This is the storage item name used inside `pallet-domain-id`
663            "SelfDomainId".as_bytes(),
664        )
665        .to_vec(),
666    )
667}
668
669/// `DomainInstanceData` is used to construct the genesis storage of domain instance chain
670#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
671pub struct DomainInstanceData {
672    pub runtime_type: RuntimeType,
673    pub raw_genesis: RawGenesis,
674}
675
676#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
677pub struct DomainBundleLimit {
678    /// The max bundle size for the domain.
679    pub max_bundle_size: u32,
680    /// The max bundle weight for the domain.
681    pub max_bundle_weight: Weight,
682}
683
684/// Calculates the max bundle weight and size
685// See https://forum.subspace.network/t/on-bundle-weight-limits-sum/2277 for more details
686// about the formula
687pub fn calculate_max_bundle_weight_and_size(
688    max_domain_block_size: u32,
689    max_domain_block_weight: Weight,
690    consensus_slot_probability: (u64, u64),
691    bundle_slot_probability: (u64, u64),
692) -> Option<DomainBundleLimit> {
693    // (n1 / d1) / (n2 / d2) is equal to (n1 * d2) / (d1 * n2)
694    // This represents: bundle_slot_probability/SLOT_PROBABILITY
695    let expected_bundles_per_block = bundle_slot_probability
696        .0
697        .checked_mul(consensus_slot_probability.1)?
698        .checked_div(
699            bundle_slot_probability
700                .1
701                .checked_mul(consensus_slot_probability.0)?,
702        )?;
703
704    // set the proof size for bundle to be proof size of max domain weight
705    // so that each domain extrinsic can use the full proof size if required
706    let max_proof_size = max_domain_block_weight.proof_size();
707    let max_bundle_weight = max_domain_block_weight
708        .checked_div(expected_bundles_per_block)?
709        .set_proof_size(max_proof_size);
710
711    let max_bundle_size =
712        (max_domain_block_size as u64).checked_div(expected_bundles_per_block)? as u32;
713
714    Some(DomainBundleLimit {
715        max_bundle_size,
716        max_bundle_weight,
717    })
718}
719
720/// Checks if the signer Id hash is within the tx range
721pub fn signer_in_tx_range(bundle_vrf_hash: &U256, signer_id_hash: &U256, tx_range: &U256) -> bool {
722    let distance_from_vrf_hash = bidirectional_distance(bundle_vrf_hash, signer_id_hash);
723    distance_from_vrf_hash <= (*tx_range / 2)
724}
725
726/// Receipt invalidity type.
727#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
728pub enum InvalidReceipt {
729    /// The field `invalid_bundles` in [`ExecutionReceiptFor`] is invalid.
730    InvalidBundles,
731}
732
733#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
734pub enum ReceiptValidity {
735    Valid,
736    Invalid(InvalidReceipt),
737}
738
739/// Empty extrinsics root.
740pub const EMPTY_EXTRINSIC_ROOT: ExtrinsicsRoot = ExtrinsicsRoot {
741    0: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"),
742};
743
744pub fn derive_domain_block_hash<DomainHeader: HeaderT>(
745    domain_block_number: DomainHeader::Number,
746    extrinsics_root: DomainHeader::Hash,
747    state_root: DomainHeader::Hash,
748    parent_domain_block_hash: DomainHeader::Hash,
749    digest: Digest,
750) -> DomainHeader::Hash {
751    let domain_header = DomainHeader::new(
752        domain_block_number,
753        extrinsics_root,
754        state_root,
755        parent_domain_block_hash,
756        digest,
757    );
758
759    domain_header.hash()
760}
761
762/// Represents the extrinsic either as full data or hash of the data.
763#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
764pub enum ExtrinsicDigest {
765    /// Actual extrinsic data that is inlined since it is less than 33 bytes.
766    Data(Vec<u8>),
767    /// Extrinsic Hash.
768    Hash(H256),
769}
770
771impl ExtrinsicDigest {
772    pub fn new<Layout: TrieLayout>(ext: Vec<u8>) -> Self
773    where
774        Layout::Hash: HashT,
775        <Layout::Hash as HashT>::Output: Into<H256>,
776    {
777        if let Some(threshold) = Layout::MAX_INLINE_VALUE {
778            if ext.len() >= threshold as usize {
779                ExtrinsicDigest::Hash(Layout::Hash::hash(&ext).into())
780            } else {
781                ExtrinsicDigest::Data(ext)
782            }
783        } else {
784            ExtrinsicDigest::Data(ext)
785        }
786    }
787}
788
789/// Trait that tracks the balances on Domains.
790pub trait DomainsTransfersTracker<Balance> {
791    type Error;
792
793    /// Initializes the domain balance
794    fn initialize_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
795
796    /// Notes a transfer between chains.
797    /// Balance on from_chain_id is reduced if it is a domain chain
798    fn note_transfer(
799        from_chain_id: ChainId,
800        to_chain_id: ChainId,
801        amount: Balance,
802    ) -> Result<(), Self::Error>;
803
804    /// Confirms a transfer between chains.
805    fn confirm_transfer(
806        from_chain_id: ChainId,
807        to_chain_id: ChainId,
808        amount: Balance,
809    ) -> Result<(), Self::Error>;
810
811    /// Claims a rejected transfer between chains.
812    fn claim_rejected_transfer(
813        from_chain_id: ChainId,
814        to_chain_id: ChainId,
815        amount: Balance,
816    ) -> Result<(), Self::Error>;
817
818    /// Rejects a initiated transfer between chains.
819    fn reject_transfer(
820        from_chain_id: ChainId,
821        to_chain_id: ChainId,
822        amount: Balance,
823    ) -> Result<(), Self::Error>;
824
825    /// Reduces a given amount from the domain balance
826    fn reduce_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
827}
828
829/// Trait to check domain owner.
830pub trait DomainOwner<AccountId> {
831    /// Returns true if the account is the domain owner.
832    fn is_domain_owner(domain_id: DomainId, acc: AccountId) -> bool;
833}
834
835impl<AccountId> DomainOwner<AccountId> for () {
836    fn is_domain_owner(_domain_id: DomainId, _acc: AccountId) -> bool {
837        false
838    }
839}
840
841/// Post hook to know if the domain had bundle submitted in the previous block.
842pub trait DomainBundleSubmitted {
843    /// Called in the next block initialisation if there was a domain bundle in the previous block.
844    /// This hook if called for domain represents that there is a new domain block for parent consensus block.
845    fn domain_bundle_submitted(domain_id: DomainId);
846}
847
848impl DomainBundleSubmitted for () {
849    fn domain_bundle_submitted(_domain_id: DomainId) {}
850}
851
852/// A hook to call after a domain is instantiated
853pub trait OnDomainInstantiated {
854    fn on_domain_instantiated(domain_id: DomainId);
855}
856
857impl OnDomainInstantiated for () {
858    fn on_domain_instantiated(_domain_id: DomainId) {}
859}
860
861/// Domain chains allowlist updates.
862#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
863pub struct DomainAllowlistUpdates {
864    /// Chains that are allowed to open a channel with this chain.
865    pub allow_chains: BTreeSet<ChainId>,
866    /// Chains that are not allowed to open a channel with this chain.
867    pub remove_chains: BTreeSet<ChainId>,
868}
869
870impl DomainAllowlistUpdates {
871    pub fn is_empty(&self) -> bool {
872        self.allow_chains.is_empty() && self.remove_chains.is_empty()
873    }
874
875    pub fn clear(&mut self) {
876        self.allow_chains.clear();
877        self.remove_chains.clear();
878    }
879}
880
881/// Domain Sudo runtime call.
882///
883/// This structure exists because we need to generate a storage proof for FP
884/// and Storage shouldn't be None. So each domain must always hold this value even if
885/// there is an empty runtime call inside
886#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
887pub struct DomainSudoCall {
888    pub maybe_call: Option<Vec<u8>>,
889}
890
891impl DomainSudoCall {
892    pub fn clear(&mut self) {
893        self.maybe_call.take();
894    }
895}
896
897/// EVM Domain "update contract creation allowed by" runtime call.
898///
899/// This structure exists because we need to generate a storage proof for FP
900/// and Storage shouldn't be None. So each domain must always hold this value even if
901/// there is an empty runtime call inside
902#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
903pub struct EvmDomainContractCreationAllowedByCall {
904    pub maybe_call: Option<PermissionedActionAllowedBy<EthereumAccountId>>,
905}
906
907impl EvmDomainContractCreationAllowedByCall {
908    pub fn clear(&mut self) {
909        self.maybe_call.take();
910    }
911}
912
913#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
914pub struct RuntimeObject<Number, Hash> {
915    pub runtime_name: String,
916    pub runtime_type: RuntimeType,
917    pub runtime_upgrades: u32,
918    pub instance_count: u32,
919    pub hash: Hash,
920    // The raw genesis storage that contains the runtime code.
921    // NOTE: don't use this field directly but `into_complete_raw_genesis` instead
922    pub raw_genesis: RawGenesis,
923    pub version: RuntimeVersion,
924    pub created_at: Number,
925    pub updated_at: Number,
926}
927
928/// Digest storage key in frame_system.
929/// Unfortunately, the digest storage is private and not possible to derive the key from it directly.
930pub fn system_digest_final_key() -> Vec<u8> {
931    frame_support::storage::storage_prefix("System".as_ref(), "Digest".as_ref()).to_vec()
932}
933
934/// Hook to handle chain rewards.
935pub trait OnChainRewards<Balance> {
936    fn on_chain_rewards(chain_id: ChainId, reward: Balance);
937}
938
939impl<Balance> OnChainRewards<Balance> for () {
940    fn on_chain_rewards(_chain_id: ChainId, _reward: Balance) {}
941}
942
943#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
944pub enum OperatorRewardSource<Number> {
945    Bundle {
946        at_block_number: Number,
947    },
948    XDMProtocolFees,
949    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
950    Dummy,
951}
952
953/// Bundle and Execution Versions.
954#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
955pub struct BundleAndExecutionReceiptVersion {
956    pub bundle_version: BundleVersion,
957    pub execution_receipt_version: ExecutionReceiptVersion,
958}
959
960/// Represents a nominator's storage fee deposit information
961#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
962pub struct StorageFeeDeposit<Balance> {
963    /// Original amount contributed to storage fees
964    pub total_deposited: Balance,
965    /// Current value adjusted for fund performance (gains/losses)
966    pub current_value: Balance,
967}
968
969/// Represents a nominator's pending deposit that hasn't been converted to shares yet
970#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
971pub struct PendingDeposit<Balance> {
972    /// The amount of the pending deposit
973    pub amount: Balance,
974    /// The epoch when this deposit will become effective
975    pub effective_epoch: EpochIndex,
976}
977
978/// Represents a nominator's pending withdrawal with unlock timing
979#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
980pub struct PendingWithdrawal<Balance, DomainBlockNumber> {
981    /// The amount of stake that will be withdrawn
982    pub stake_withdrawal_amount: Balance,
983    /// The amount of storage fee deposit that will be refunded
984    pub storage_fee_refund: Balance,
985    /// The domain block number when this withdrawal can be unlocked
986    pub unlock_at_block: DomainBlockNumber,
987}
988
989/// Complete nominator position information for a specific operator
990#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
991pub struct NominatorPosition<Balance, DomainBlockNumber, Share> {
992    /// Current value of the nominator's position (shares converted to balance using current share price)
993    pub current_staked_value: Balance,
994    /// Total shares owned by nominator
995    pub total_shares: Share,
996    /// Storage fee deposit information (original and current adjusted values)
997    pub storage_fee_deposit: StorageFeeDeposit<Balance>,
998    /// Pending deposit not yet converted to shares
999    pub pending_deposit: Option<PendingDeposit<Balance>>,
1000    /// Pending withdrawals with unlock timing
1001    pub pending_withdrawals: Vec<PendingWithdrawal<Balance, DomainBlockNumber>>,
1002}
1003
1004sp_api::decl_runtime_apis! {
1005    /// APIs used to access the domains pallet.
1006    // When updating this version, document new APIs with "Only present in API versions" comments.
1007    #[api_version(6)]
1008    pub trait DomainsApi<DomainHeader: HeaderT> {
1009        /// Submits the transaction bundle via an unsigned extrinsic.
1010        fn submit_bundle_unsigned(opaque_bundle: OpaqueBundle<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1011
1012        /// Submits a singleton receipt via an unsigned extrinsic.
1013        fn submit_receipt_unsigned(singleton_receipt: SealedSingletonReceipt<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1014
1015        /// Extracts the bundles successfully stored from the given extrinsics.
1016        fn extract_successful_bundles(
1017            domain_id: DomainId,
1018            extrinsics: Vec<Block::Extrinsic>,
1019        ) -> OpaqueBundles<Block, DomainHeader, Balance>;
1020
1021        /// Generates a randomness seed for extrinsics shuffling.
1022        fn extrinsics_shuffling_seed() -> Randomness;
1023
1024        /// Returns the current WASM bundle for the given `domain_id`.
1025        fn domain_runtime_code(domain_id: DomainId) -> Option<Vec<u8>>;
1026
1027        /// Returns the runtime id for the given `domain_id`.
1028        fn runtime_id(domain_id: DomainId) -> Option<RuntimeId>;
1029
1030        /// Returns the list of runtime upgrades in the current block.
1031        fn runtime_upgrades() -> Vec<RuntimeId>;
1032
1033        /// Returns the domain instance data for the given `domain_id`.
1034        fn domain_instance_data(domain_id: DomainId) -> Option<(DomainInstanceData, NumberFor<Block>)>;
1035
1036        /// Returns the current timestamp at the current height.
1037        fn domain_timestamp() -> Moment;
1038
1039        /// Returns the consensus transaction byte fee that will used to charge the domain
1040        /// transaction for consensus chain storage fees.
1041        fn consensus_transaction_byte_fee() -> Balance;
1042
1043        /// Returns the current Tx range for the given domain Id.
1044        fn domain_tx_range(domain_id: DomainId) -> U256;
1045
1046        /// Returns the genesis state root if not pruned.
1047        fn genesis_state_root(domain_id: DomainId) -> Option<H256>;
1048
1049        /// Returns the best execution chain number.
1050        fn head_receipt_number(domain_id: DomainId) -> HeaderNumberFor<DomainHeader>;
1051
1052        /// Returns the block number of oldest unconfirmed execution receipt.
1053        fn oldest_unconfirmed_receipt_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1054
1055        /// Returns the domain bundle limit of the given domain.
1056        fn domain_bundle_limit(domain_id: DomainId) -> Option<DomainBundleLimit>;
1057
1058        /// Returns true if there are any ERs in the challenge period with non empty extrinsics.
1059        fn non_empty_er_exists(domain_id: DomainId) -> bool;
1060
1061        /// Returns the current best block number for the domain.
1062        fn domain_best_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1063
1064        /// Returns the execution receipt with the given hash.
1065        fn execution_receipt(receipt_hash: HeaderHashFor<DomainHeader>) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1066
1067        /// Returns the current epoch and the next epoch operators of the given domain.
1068        fn domain_operators(domain_id: DomainId) -> Option<(BTreeMap<OperatorId, Balance>, Vec<OperatorId>)>;
1069
1070        /// Returns the execution receipt hash of the given domain and domain block number.
1071        fn receipt_hash(domain_id: DomainId, domain_number: HeaderNumberFor<DomainHeader>) -> Option<HeaderHashFor<DomainHeader>>;
1072
1073        /// Returns the latest confirmed domain block number and hash.
1074        fn latest_confirmed_domain_block(domain_id: DomainId) -> Option<(HeaderNumberFor<DomainHeader>, HeaderHashFor<DomainHeader>)>;
1075
1076        /// Returns true if the receipt exists and is going to be pruned
1077        fn is_bad_er_pending_to_prune(domain_id: DomainId, receipt_hash: HeaderHashFor<DomainHeader>) -> bool;
1078
1079        /// Returns the balance of the storage fund account.
1080        fn storage_fund_account_balance(operator_id: OperatorId) -> Balance;
1081
1082        /// Returns true if the given domain's runtime code has been upgraded since `at`.
1083        fn is_domain_runtime_upgraded_since(domain_id: DomainId, at: NumberFor<Block>) -> Option<bool>;
1084
1085        /// Returns the domain sudo call for the given domain, if any.
1086        fn domain_sudo_call(domain_id: DomainId) -> Option<Vec<u8>>;
1087
1088        /// Returns the "set contract creation allowed by" call for the given EVM domain, if any.
1089        fn evm_domain_contract_creation_allowed_by_call(domain_id: DomainId) -> Option<PermissionedActionAllowedBy<EthereumAccountId>>;
1090
1091        /// Returns the last confirmed domain block execution receipt.
1092        fn last_confirmed_domain_block_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1093
1094        /// Returns the current bundle version that is accepted by runtime.
1095        fn current_bundle_and_execution_receipt_version() -> BundleAndExecutionReceiptVersion;
1096
1097        /// Returns genesis execution receipt for domains.
1098        fn genesis_execution_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1099
1100        /// Returns the complete nominator position for a given operator and account.
1101        ///
1102        /// This calculates the total position including:
1103        /// - Current stake value (converted from shares using current share price)
1104        /// - Total storage fee deposits (known + pending)
1105        /// - Pending deposits (not yet converted to shares)
1106        /// - Pending withdrawals (with unlock timing)
1107        ///
1108        fn nominator_position(
1109            operator_id: OperatorId,
1110            nominator_account: sp_runtime::AccountId32,
1111        ) -> Option<NominatorPosition<Balance, HeaderNumberFor<DomainHeader>, Balance>>;
1112
1113        /// Returns the block pruning depth for domains
1114        /// Available from Api version 6.
1115        fn block_pruning_depth() -> NumberFor<Block>;
1116    }
1117
1118    pub trait BundleProducerElectionApi<Balance: Encode + Decode> {
1119        fn bundle_producer_election_params(domain_id: DomainId) -> Option<BundleProducerElectionParams<Balance>>;
1120
1121        fn operator(operator_id: OperatorId) -> Option<(OperatorPublicKey, Balance)>;
1122    }
1123}