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