sp_domains/
lib.rs

1//! Primitives for domains pallet.
2
3#![cfg_attr(not(feature = "std"), no_std)]
4
5pub mod bundle_producer_election;
6pub mod core_api;
7pub mod extrinsics;
8pub mod merkle_tree;
9pub mod proof_provider_and_verifier;
10pub mod storage;
11#[cfg(test)]
12mod tests;
13pub mod valued_trie;
14
15#[cfg(not(feature = "std"))]
16extern crate alloc;
17
18use crate::storage::{RawGenesis, StorageKey};
19#[cfg(not(feature = "std"))]
20use alloc::collections::BTreeSet;
21#[cfg(not(feature = "std"))]
22use alloc::string::String;
23#[cfg(not(feature = "std"))]
24use alloc::vec::Vec;
25use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionError};
26use core::num::ParseIntError;
27use core::ops::{Add, Sub};
28use core::str::FromStr;
29use domain_runtime_primitives::{EthereumAccountId, MultiAccountId};
30use frame_support::storage::storage_prefix;
31use frame_support::{Blake2_128Concat, StorageHasher};
32use hex_literal::hex;
33use parity_scale_codec::{Codec, Decode, Encode, MaxEncodedLen};
34use scale_info::TypeInfo;
35use serde::{Deserialize, Serialize};
36use sp_core::H256;
37use sp_core::crypto::KeyTypeId;
38use sp_core::sr25519::vrf::VrfSignature;
39#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
40use sp_core::sr25519::vrf::{VrfPreOutput, VrfProof};
41use sp_runtime::generic::OpaqueDigestItemId;
42use sp_runtime::traits::{
43    BlakeTwo256, CheckedAdd, Hash as HashT, Header as HeaderT, NumberFor, Zero,
44};
45use sp_runtime::{Digest, DigestItem, OpaqueExtrinsic, Percent};
46use sp_runtime_interface::pass_by;
47use sp_runtime_interface::pass_by::PassBy;
48use sp_std::collections::btree_map::BTreeMap;
49use sp_std::fmt::{Display, Formatter};
50use sp_trie::TrieLayout;
51use sp_version::RuntimeVersion;
52use sp_weights::Weight;
53#[cfg(feature = "std")]
54use std::collections::BTreeSet;
55use subspace_core_primitives::hashes::{Blake3Hash, blake3_hash};
56use subspace_core_primitives::pot::PotOutput;
57use subspace_core_primitives::solutions::bidirectional_distance;
58use subspace_core_primitives::{Randomness, U256};
59use subspace_runtime_primitives::{Balance, BlockHashFor, Moment};
60
61/// Key type for Operator.
62pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"oper");
63
64/// Extrinsics shuffling seed
65pub const DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT: &[u8] = b"extrinsics-shuffling-seed";
66
67mod app {
68    use super::KEY_TYPE;
69    use sp_application_crypto::{app_crypto, sr25519};
70
71    app_crypto!(sr25519, KEY_TYPE);
72}
73
74// TODO: this runtime constant is not support to update, see https://github.com/autonomys/subspace/issues/2712
75// for more detail about the problem and what we need to do to support it.
76//
77// The domain storage fee multiplier used to charge a higher storage fee to the domain
78// transaction to even out the duplicated/illegal domain transaction storage cost, which
79// can not be eliminated right now.
80pub const DOMAIN_STORAGE_FEE_MULTIPLIER: Balance = 3;
81
82/// An operator authority signature.
83pub type OperatorSignature = app::Signature;
84
85/// An operator authority keypair. Necessarily equivalent to the schnorrkel public key used in
86/// the main executor module. If that ever changes, then this must, too.
87#[cfg(feature = "std")]
88pub type OperatorPair = app::Pair;
89
90/// An operator authority identifier.
91pub type OperatorPublicKey = app::Public;
92
93/// A type that implements `BoundToRuntimeAppPublic`, used for operator signing key.
94pub struct OperatorKey;
95
96impl sp_runtime::BoundToRuntimeAppPublic for OperatorKey {
97    type Public = OperatorPublicKey;
98}
99
100/// Stake weight in the domain bundle election.
101///
102/// Derived from the Balance and can't be smaller than u128.
103pub type StakeWeight = u128;
104
105/// The Trie root of all extrinsics included in a bundle.
106pub type ExtrinsicsRoot = H256;
107
108/// Type alias for Header Hashing.
109pub type HeaderHashingFor<Header> = <Header as HeaderT>::Hashing;
110/// Type alias for Header number.
111pub type HeaderNumberFor<Header> = <Header as HeaderT>::Number;
112/// Type alias for Header hash.
113pub type HeaderHashFor<Header> = <Header as HeaderT>::Hash;
114
115/// Unique identifier of a domain.
116#[derive(
117    Clone,
118    Copy,
119    Debug,
120    Hash,
121    Default,
122    Eq,
123    PartialEq,
124    Ord,
125    PartialOrd,
126    Encode,
127    Decode,
128    TypeInfo,
129    Serialize,
130    Deserialize,
131    MaxEncodedLen,
132)]
133pub struct DomainId(u32);
134
135impl From<u32> for DomainId {
136    #[inline]
137    fn from(x: u32) -> Self {
138        Self(x)
139    }
140}
141
142impl From<DomainId> for u32 {
143    #[inline]
144    fn from(domain_id: DomainId) -> Self {
145        domain_id.0
146    }
147}
148
149impl FromStr for DomainId {
150    type Err = ParseIntError;
151
152    fn from_str(s: &str) -> Result<Self, Self::Err> {
153        s.parse::<u32>().map(Into::into)
154    }
155}
156
157impl Add<DomainId> for DomainId {
158    type Output = Self;
159
160    fn add(self, other: DomainId) -> Self {
161        Self(self.0 + other.0)
162    }
163}
164
165impl Sub<DomainId> for DomainId {
166    type Output = Self;
167
168    fn sub(self, other: DomainId) -> Self {
169        Self(self.0 - other.0)
170    }
171}
172
173impl CheckedAdd for DomainId {
174    fn checked_add(&self, rhs: &Self) -> Option<Self> {
175        self.0.checked_add(rhs.0).map(Self)
176    }
177}
178
179impl DomainId {
180    /// Creates a [`DomainId`].
181    pub const fn new(id: u32) -> Self {
182        Self(id)
183    }
184
185    /// Converts the inner integer to little-endian bytes.
186    pub fn to_le_bytes(&self) -> [u8; 4] {
187        self.0.to_le_bytes()
188    }
189}
190
191impl Display for DomainId {
192    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
193        self.0.fmt(f)
194    }
195}
196
197impl PassBy for DomainId {
198    type PassBy = pass_by::Codec<Self>;
199}
200
201/// Identifier of a chain.
202#[derive(
203    Clone,
204    Copy,
205    Debug,
206    Hash,
207    Eq,
208    PartialEq,
209    Ord,
210    PartialOrd,
211    Encode,
212    Decode,
213    TypeInfo,
214    Serialize,
215    Deserialize,
216    MaxEncodedLen,
217)]
218pub enum ChainId {
219    Consensus,
220    Domain(DomainId),
221}
222
223impl ChainId {
224    #[inline]
225    pub fn consensus_chain_id() -> Self {
226        Self::Consensus
227    }
228
229    #[inline]
230    pub fn is_consensus_chain(&self) -> bool {
231        match self {
232            ChainId::Consensus => true,
233            ChainId::Domain(_) => false,
234        }
235    }
236
237    #[inline]
238    pub fn maybe_domain_chain(&self) -> Option<DomainId> {
239        match self {
240            ChainId::Consensus => None,
241            ChainId::Domain(domain_id) => Some(*domain_id),
242        }
243    }
244}
245
246impl From<u32> for ChainId {
247    #[inline]
248    fn from(x: u32) -> Self {
249        Self::Domain(DomainId::new(x))
250    }
251}
252
253impl From<DomainId> for ChainId {
254    #[inline]
255    fn from(x: DomainId) -> Self {
256        Self::Domain(x)
257    }
258}
259
260#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)]
261pub struct BlockFees<Balance> {
262    /// The consensus chain storage fee
263    pub consensus_storage_fee: Balance,
264    /// The domain execution fee including the storage and compute fee on domain chain,
265    /// tip, and the XDM reward.
266    pub domain_execution_fee: Balance,
267    /// Burned balances on domain chain
268    pub burned_balance: Balance,
269    /// Rewards for the chain.
270    pub chain_rewards: BTreeMap<ChainId, Balance>,
271}
272
273impl<Balance> BlockFees<Balance>
274where
275    Balance: CheckedAdd + Zero,
276{
277    pub fn new(
278        domain_execution_fee: Balance,
279        consensus_storage_fee: Balance,
280        burned_balance: Balance,
281        chain_rewards: BTreeMap<ChainId, Balance>,
282    ) -> Self {
283        BlockFees {
284            consensus_storage_fee,
285            domain_execution_fee,
286            burned_balance,
287            chain_rewards,
288        }
289    }
290
291    /// Returns the total fees that was collected and burned on the Domain.
292    pub fn total_fees(&self) -> Option<Balance> {
293        let total_chain_reward = self
294            .chain_rewards
295            .values()
296            .try_fold(Zero::zero(), |acc: Balance, cr| acc.checked_add(cr))?;
297        self.consensus_storage_fee
298            .checked_add(&self.domain_execution_fee)
299            .and_then(|balance| balance.checked_add(&self.burned_balance))
300            .and_then(|balance| balance.checked_add(&total_chain_reward))
301    }
302}
303
304/// Type that holds the transfers(in/out) for a given chain.
305#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)]
306pub struct Transfers<Balance> {
307    /// Total transfers that came into the domain.
308    pub transfers_in: BTreeMap<ChainId, Balance>,
309    /// Total transfers that went out of the domain.
310    pub transfers_out: BTreeMap<ChainId, Balance>,
311    /// Total transfers from this domain that were reverted.
312    pub rejected_transfers_claimed: BTreeMap<ChainId, Balance>,
313    /// Total transfers to this domain that were rejected.
314    pub transfers_rejected: BTreeMap<ChainId, Balance>,
315}
316
317impl<Balance> Transfers<Balance> {
318    pub fn is_valid(&self, chain_id: ChainId) -> bool {
319        !self.transfers_rejected.contains_key(&chain_id)
320            && !self.transfers_in.contains_key(&chain_id)
321            && !self.transfers_out.contains_key(&chain_id)
322            && !self.rejected_transfers_claimed.contains_key(&chain_id)
323    }
324}
325
326// TODO: this runtime constant is not support to update, see https://github.com/autonomys/subspace/issues/2712
327// for more detail about the problem and what we need to do to support it.
328//
329/// Initial tx range = U256::MAX / INITIAL_DOMAIN_TX_RANGE.
330pub const INITIAL_DOMAIN_TX_RANGE: u64 = 3;
331
332#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
333pub struct BundleHeader<Number, Hash, DomainHeader: HeaderT, Balance> {
334    /// Proof of bundle producer election.
335    pub proof_of_election: ProofOfElection,
336    /// Execution receipt that should extend the receipt chain or add confirmations
337    /// to the head receipt.
338    pub receipt: ExecutionReceipt<
339        Number,
340        Hash,
341        HeaderNumberFor<DomainHeader>,
342        HeaderHashFor<DomainHeader>,
343        Balance,
344    >,
345    /// The total (estimated) weight of all extrinsics in the bundle.
346    ///
347    /// Used to prevent overloading the bundle with compute.
348    pub estimated_bundle_weight: Weight,
349    /// The Merkle root of all new extrinsics included in this bundle.
350    pub bundle_extrinsics_root: HeaderHashFor<DomainHeader>,
351}
352
353impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
354    BundleHeader<Number, Hash, DomainHeader, Balance>
355{
356    /// Returns the hash of this header.
357    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
358        HeaderHashingFor::<DomainHeader>::hash_of(self)
359    }
360}
361
362/// Header of bundle.
363#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
364pub struct SealedBundleHeader<Number, Hash, DomainHeader: HeaderT, Balance> {
365    /// Unsealed header.
366    pub header: BundleHeader<Number, Hash, DomainHeader, Balance>,
367    /// Signature of the bundle.
368    pub signature: OperatorSignature,
369}
370
371impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
372    SealedBundleHeader<Number, Hash, DomainHeader, Balance>
373{
374    /// Constructs a new instance of [`SealedBundleHeader`].
375    pub fn new(
376        header: BundleHeader<Number, Hash, DomainHeader, Balance>,
377        signature: OperatorSignature,
378    ) -> Self {
379        Self { header, signature }
380    }
381
382    /// Returns the hash of the inner unsealed header.
383    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
384        self.header.hash()
385    }
386
387    /// Returns the hash of this header.
388    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
389        HeaderHashingFor::<DomainHeader>::hash_of(self)
390    }
391
392    pub fn slot_number(&self) -> u64 {
393        self.header.proof_of_election.slot_number
394    }
395}
396
397/// Domain bundle.
398#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
399pub struct Bundle<Extrinsic, Number, Hash, DomainHeader: HeaderT, Balance> {
400    /// Sealed bundle header.
401    pub sealed_header: SealedBundleHeader<Number, Hash, DomainHeader, Balance>,
402    /// The accompanying extrinsics.
403    pub extrinsics: Vec<Extrinsic>,
404}
405
406impl<Extrinsic: Encode, Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
407    Bundle<Extrinsic, Number, Hash, DomainHeader, Balance>
408{
409    /// Returns the hash of this bundle.
410    pub fn hash(&self) -> H256 {
411        BlakeTwo256::hash_of(self)
412    }
413
414    /// Returns the domain_id of this bundle.
415    pub fn domain_id(&self) -> DomainId {
416        self.sealed_header.header.proof_of_election.domain_id
417    }
418
419    /// Return the `bundle_extrinsics_root`
420    pub fn extrinsics_root(&self) -> HeaderHashFor<DomainHeader> {
421        self.sealed_header.header.bundle_extrinsics_root
422    }
423
424    /// Return the `operator_id`
425    pub fn operator_id(&self) -> OperatorId {
426        self.sealed_header.header.proof_of_election.operator_id
427    }
428
429    /// Return a reference of the execution receipt.
430    pub fn receipt(
431        &self,
432    ) -> &ExecutionReceipt<
433        Number,
434        Hash,
435        HeaderNumberFor<DomainHeader>,
436        HeaderHashFor<DomainHeader>,
437        Balance,
438    > {
439        &self.sealed_header.header.receipt
440    }
441
442    /// Consumes [`Bundle`] to extract the execution receipt.
443    pub fn into_receipt(
444        self,
445    ) -> ExecutionReceipt<
446        Number,
447        Hash,
448        HeaderNumberFor<DomainHeader>,
449        HeaderHashFor<DomainHeader>,
450        Balance,
451    > {
452        self.sealed_header.header.receipt
453    }
454
455    /// Return the bundle size (include header and body) in bytes
456    pub fn size(&self) -> u32 {
457        self.encoded_size() as u32
458    }
459
460    /// Return the bundle body size in bytes
461    pub fn body_size(&self) -> u32 {
462        self.extrinsics
463            .iter()
464            .map(|tx| tx.encoded_size() as u32)
465            .sum::<u32>()
466    }
467
468    pub fn estimated_weight(&self) -> Weight {
469        self.sealed_header.header.estimated_bundle_weight
470    }
471
472    pub fn slot_number(&self) -> u64 {
473        self.sealed_header.header.proof_of_election.slot_number
474    }
475}
476
477/// Bundle with opaque extrinsics.
478pub type OpaqueBundle<Number, Hash, DomainHeader, Balance> =
479    Bundle<OpaqueExtrinsic, Number, Hash, DomainHeader, Balance>;
480
481/// List of [`OpaqueBundle`].
482pub type OpaqueBundles<Block, DomainHeader, Balance> =
483    Vec<OpaqueBundle<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, Balance>>;
484
485impl<Extrinsic: Encode, Number, Hash, DomainHeader: HeaderT, Balance>
486    Bundle<Extrinsic, Number, Hash, DomainHeader, Balance>
487{
488    /// Convert a bundle with generic extrinsic to a bundle with opaque extrinsic.
489    pub fn into_opaque_bundle(self) -> OpaqueBundle<Number, Hash, DomainHeader, Balance> {
490        let Bundle {
491            sealed_header,
492            extrinsics,
493        } = self;
494        let opaque_extrinsics = extrinsics
495            .into_iter()
496            .map(|xt| {
497                OpaqueExtrinsic::from_bytes(&xt.encode())
498                    .expect("We have just encoded a valid extrinsic; qed")
499            })
500            .collect();
501        OpaqueBundle {
502            sealed_header,
503            extrinsics: opaque_extrinsics,
504        }
505    }
506}
507
508#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
509pub fn dummy_opaque_bundle<
510    Number: Encode,
511    Hash: Default + Encode,
512    DomainHeader: HeaderT,
513    Balance: Encode,
514>(
515    domain_id: DomainId,
516    operator_id: OperatorId,
517    receipt: ExecutionReceipt<
518        Number,
519        Hash,
520        HeaderNumberFor<DomainHeader>,
521        HeaderHashFor<DomainHeader>,
522        Balance,
523    >,
524) -> OpaqueBundle<Number, Hash, DomainHeader, Balance> {
525    use sp_core::crypto::UncheckedFrom;
526
527    let header = BundleHeader {
528        proof_of_election: ProofOfElection::dummy(domain_id, operator_id),
529        receipt,
530        estimated_bundle_weight: Default::default(),
531        bundle_extrinsics_root: Default::default(),
532    };
533    let signature = OperatorSignature::unchecked_from([0u8; 64]);
534
535    OpaqueBundle {
536        sealed_header: SealedBundleHeader::new(header, signature),
537        extrinsics: Vec::new(),
538    }
539}
540
541/// A digest of the bundle
542#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
543pub struct BundleDigest<Hash> {
544    /// The hash of the bundle header
545    pub header_hash: Hash,
546    /// The Merkle root of all new extrinsics included in this bundle.
547    pub extrinsics_root: Hash,
548    /// The size of the bundle body in bytes.
549    pub size: u32,
550}
551
552/// Receipt of a domain block execution.
553#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
554pub struct ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance> {
555    /// The index of the current domain block that forms the basis of this ER.
556    pub domain_block_number: DomainNumber,
557    /// The block hash corresponding to `domain_block_number`.
558    pub domain_block_hash: DomainHash,
559    /// Extrinsic root field of the header of domain block referenced by this ER.
560    pub domain_block_extrinsic_root: DomainHash,
561    /// The hash of the ER for the last domain block.
562    pub parent_domain_block_receipt_hash: DomainHash,
563    /// A pointer to the consensus block index which contains all of the bundles that were used to derive and
564    /// order all extrinsics executed by the current domain block for this ER.
565    pub consensus_block_number: Number,
566    /// The block hash corresponding to `consensus_block_number`.
567    pub consensus_block_hash: Hash,
568    /// All the bundles that being included in the consensus block.
569    pub inboxed_bundles: Vec<InboxedBundle<DomainHash>>,
570    /// The final state root for the current domain block reflected by this ER.
571    ///
572    /// Used for verifying storage proofs for domains.
573    pub final_state_root: DomainHash,
574    /// List of storage roots collected during the domain block execution.
575    pub execution_trace: Vec<DomainHash>,
576    /// The Merkle root of the execution trace for the current domain block.
577    ///
578    /// Used for verifying fraud proofs.
579    pub execution_trace_root: H256,
580    /// Compute and Domain storage fees are shared across operators and Consensus
581    /// storage fees are given to the consensus block author.
582    pub block_fees: BlockFees<Balance>,
583    /// List of transfers from this Domain to other chains
584    pub transfers: Transfers<Balance>,
585}
586
587impl<Number, Hash, DomainNumber, DomainHash, Balance>
588    ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
589{
590    pub fn bundles_extrinsics_roots(&self) -> Vec<&DomainHash> {
591        self.inboxed_bundles
592            .iter()
593            .map(|b| &b.extrinsics_root)
594            .collect()
595    }
596
597    pub fn valid_bundle_digest_at(&self, index: usize) -> Option<DomainHash>
598    where
599        DomainHash: Copy,
600    {
601        match self.inboxed_bundles.get(index).map(|ib| &ib.bundle) {
602            Some(BundleValidity::Valid(bundle_digest_hash)) => Some(*bundle_digest_hash),
603            _ => None,
604        }
605    }
606
607    pub fn valid_bundle_digests(&self) -> Vec<DomainHash>
608    where
609        DomainHash: Copy,
610    {
611        self.inboxed_bundles
612            .iter()
613            .filter_map(|b| match b.bundle {
614                BundleValidity::Valid(bundle_digest_hash) => Some(bundle_digest_hash),
615                BundleValidity::Invalid(_) => None,
616            })
617            .collect()
618    }
619
620    pub fn valid_bundle_indexes(&self) -> Vec<u32> {
621        self.inboxed_bundles
622            .iter()
623            .enumerate()
624            .filter_map(|(index, b)| match b.bundle {
625                BundleValidity::Valid(_) => Some(index as u32),
626                BundleValidity::Invalid(_) => None,
627            })
628            .collect()
629    }
630}
631
632impl<
633    Number: Encode + Zero,
634    Hash: Encode + Default,
635    DomainNumber: Encode + Zero,
636    DomainHash: Clone + Encode + Default,
637    Balance: Encode + Zero + Default,
638> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
639{
640    /// Returns the hash of this execution receipt.
641    pub fn hash<DomainHashing: HashT<Output = DomainHash>>(&self) -> DomainHash {
642        DomainHashing::hash_of(self)
643    }
644
645    pub fn genesis(
646        genesis_state_root: DomainHash,
647        genesis_extrinsic_root: DomainHash,
648        genesis_domain_block_hash: DomainHash,
649    ) -> Self {
650        ExecutionReceipt {
651            domain_block_number: Zero::zero(),
652            domain_block_hash: genesis_domain_block_hash,
653            domain_block_extrinsic_root: genesis_extrinsic_root,
654            parent_domain_block_receipt_hash: Default::default(),
655            consensus_block_hash: Default::default(),
656            consensus_block_number: Zero::zero(),
657            inboxed_bundles: Vec::new(),
658            final_state_root: genesis_state_root.clone(),
659            execution_trace: sp_std::vec![genesis_state_root],
660            execution_trace_root: Default::default(),
661            block_fees: Default::default(),
662            transfers: Default::default(),
663        }
664    }
665
666    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
667    pub fn dummy<DomainHashing>(
668        consensus_block_number: Number,
669        consensus_block_hash: Hash,
670        domain_block_number: DomainNumber,
671        parent_domain_block_receipt_hash: DomainHash,
672    ) -> ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>
673    where
674        DomainHashing: HashT<Output = DomainHash>,
675    {
676        let execution_trace = sp_std::vec![Default::default(), Default::default()];
677        let execution_trace_root = {
678            let trace: Vec<[u8; 32]> = execution_trace
679                .iter()
680                .map(|r: &DomainHash| r.encode().try_into().expect("H256 must fit into [u8; 32]"))
681                .collect();
682            crate::merkle_tree::MerkleTree::from_leaves(trace.as_slice())
683                .root()
684                .expect("Compute merkle root of trace should success")
685                .into()
686        };
687        ExecutionReceipt {
688            domain_block_number,
689            domain_block_hash: Default::default(),
690            domain_block_extrinsic_root: Default::default(),
691            parent_domain_block_receipt_hash,
692            consensus_block_number,
693            consensus_block_hash,
694            inboxed_bundles: sp_std::vec![InboxedBundle::dummy(Default::default())],
695            final_state_root: Default::default(),
696            execution_trace,
697            execution_trace_root,
698            block_fees: Default::default(),
699            transfers: Default::default(),
700        }
701    }
702}
703
704#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
705pub struct ProofOfElection {
706    /// Domain id.
707    pub domain_id: DomainId,
708    /// The slot number.
709    pub slot_number: u64,
710    /// The PoT output for `slot_number`.
711    pub proof_of_time: PotOutput,
712    /// VRF signature.
713    pub vrf_signature: VrfSignature,
714    /// Operator index in the OperatorRegistry.
715    pub operator_id: OperatorId,
716}
717
718impl ProofOfElection {
719    pub fn verify_vrf_signature(
720        &self,
721        operator_signing_key: &OperatorPublicKey,
722    ) -> Result<(), ProofOfElectionError> {
723        let global_challenge = self
724            .proof_of_time
725            .derive_global_randomness()
726            .derive_global_challenge(self.slot_number);
727        bundle_producer_election::verify_vrf_signature(
728            self.domain_id,
729            operator_signing_key,
730            &self.vrf_signature,
731            &global_challenge,
732        )
733    }
734
735    /// Computes the VRF hash.
736    pub fn vrf_hash(&self) -> Blake3Hash {
737        let mut bytes = self.vrf_signature.pre_output.encode();
738        bytes.append(&mut self.vrf_signature.proof.encode());
739        blake3_hash(&bytes)
740    }
741
742    pub fn slot_number(&self) -> u64 {
743        self.slot_number
744    }
745}
746
747impl ProofOfElection {
748    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
749    pub fn dummy(domain_id: DomainId, operator_id: OperatorId) -> Self {
750        let output_bytes = sp_std::vec![0u8; VrfPreOutput::max_encoded_len()];
751        let proof_bytes = sp_std::vec![0u8; VrfProof::max_encoded_len()];
752        let vrf_signature = VrfSignature {
753            pre_output: VrfPreOutput::decode(&mut output_bytes.as_slice()).unwrap(),
754            proof: VrfProof::decode(&mut proof_bytes.as_slice()).unwrap(),
755        };
756        Self {
757            domain_id,
758            slot_number: 0u64,
759            proof_of_time: PotOutput::default(),
760            vrf_signature,
761            operator_id,
762        }
763    }
764}
765
766/// Singleton receipt submit along when there is a gap between `domain_best_number`
767/// and `HeadReceiptNumber`
768#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
769pub struct SingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
770    /// Proof of receipt producer election.
771    pub proof_of_election: ProofOfElection,
772    /// The receipt to submit
773    pub receipt: ExecutionReceipt<
774        Number,
775        Hash,
776        HeaderNumberFor<DomainHeader>,
777        HeaderHashFor<DomainHeader>,
778        Balance,
779    >,
780}
781
782impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
783    SingletonReceipt<Number, Hash, DomainHeader, Balance>
784{
785    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
786        HeaderHashingFor::<DomainHeader>::hash_of(&self)
787    }
788}
789
790#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
791pub struct SealedSingletonReceipt<Number, Hash, DomainHeader: HeaderT, Balance> {
792    /// A collection of the receipt.
793    pub singleton_receipt: SingletonReceipt<Number, Hash, DomainHeader, Balance>,
794    /// Signature of the receipt bundle.
795    pub signature: OperatorSignature,
796}
797
798impl<Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
799    SealedSingletonReceipt<Number, Hash, DomainHeader, Balance>
800{
801    /// Returns the `domain_id`
802    pub fn domain_id(&self) -> DomainId {
803        self.singleton_receipt.proof_of_election.domain_id
804    }
805
806    /// Return the `operator_id`
807    pub fn operator_id(&self) -> OperatorId {
808        self.singleton_receipt.proof_of_election.operator_id
809    }
810
811    /// Return the `slot_number` of the `proof_of_election`
812    pub fn slot_number(&self) -> u64 {
813        self.singleton_receipt.proof_of_election.slot_number
814    }
815
816    /// Return the receipt
817    pub fn receipt(
818        &self,
819    ) -> &ExecutionReceipt<
820        Number,
821        Hash,
822        HeaderNumberFor<DomainHeader>,
823        HeaderHashFor<DomainHeader>,
824        Balance,
825    > {
826        &self.singleton_receipt.receipt
827    }
828
829    /// Consume this `SealedSingletonReceipt` and return the receipt
830    pub fn into_receipt(
831        self,
832    ) -> ExecutionReceipt<
833        Number,
834        Hash,
835        HeaderNumberFor<DomainHeader>,
836        HeaderHashFor<DomainHeader>,
837        Balance,
838    > {
839        self.singleton_receipt.receipt
840    }
841
842    /// Returns the hash of `SingletonReceipt`
843    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
844        HeaderHashingFor::<DomainHeader>::hash_of(&self.singleton_receipt)
845    }
846
847    /// Return the encode size of `SealedSingletonReceipt`
848    pub fn size(&self) -> u32 {
849        self.encoded_size() as u32
850    }
851}
852
853/// Type that represents an operator allow list for Domains.
854#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
855pub enum OperatorAllowList<AccountId: Ord> {
856    /// Anyone can operate for this domain.
857    Anyone,
858    /// Only the specific operators are allowed to operate the domain.
859    /// This essentially makes the domain permissioned.
860    Operators(BTreeSet<AccountId>),
861}
862
863impl<AccountId: Ord> OperatorAllowList<AccountId> {
864    /// Returns true if the allow list is either `Anyone` or the operator is part of the allowed operator list.
865    pub fn is_operator_allowed(&self, operator: &AccountId) -> bool {
866        match self {
867            OperatorAllowList::Anyone => true,
868            OperatorAllowList::Operators(allowed_operators) => allowed_operators.contains(operator),
869        }
870    }
871}
872
873/// Permissioned actions allowed by either specific accounts or anyone.
874#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
875pub enum PermissionedActionAllowedBy<AccountId: Codec + Clone> {
876    Accounts(Vec<AccountId>),
877    Anyone,
878}
879
880impl<AccountId: Codec + PartialEq + Clone> PermissionedActionAllowedBy<AccountId> {
881    pub fn is_allowed(&self, who: &AccountId) -> bool {
882        match self {
883            PermissionedActionAllowedBy::Accounts(accounts) => accounts.contains(who),
884            PermissionedActionAllowedBy::Anyone => true,
885        }
886    }
887
888    pub fn is_anyone_allowed(&self) -> bool {
889        matches!(self, PermissionedActionAllowedBy::Anyone)
890    }
891}
892
893/// EVM-specific domain type (and associated data).
894#[derive(
895    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
896)]
897pub enum EvmType {
898    #[default]
899    /// An EVM domain where any account can create contracts.
900    Public,
901    /// An EVM domain with a contract creation allow list.
902    Private {
903        /// Accounts initially allowed to create contracts on a private EVM domain.
904        /// The domain owner can update this list using a pallet-domains call (or there's a sudo call).
905        initial_contract_creation_allow_list: PermissionedActionAllowedBy<EthereumAccountId>,
906    },
907}
908
909impl EvmType {
910    /// Returns the initial contract creation allow list, or `None` if this is a public EVM domain.
911    pub fn initial_contract_creation_allow_list(
912        &self,
913    ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
914        match self {
915            EvmType::Public => None,
916            EvmType::Private {
917                initial_contract_creation_allow_list,
918            } => Some(initial_contract_creation_allow_list),
919        }
920    }
921
922    /// Returns true if the EVM domain is public.
923    pub fn is_public_evm_domain(&self) -> bool {
924        matches!(self, EvmType::Public)
925    }
926
927    /// Returns true if the EVM domain is private.
928    pub fn is_private_evm_domain(&self) -> bool {
929        matches!(self, EvmType::Private { .. })
930    }
931}
932
933/// EVM-specific domain runtime config.
934#[derive(
935    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
936)]
937pub struct EvmDomainRuntimeConfig {
938    pub evm_type: EvmType,
939}
940
941/// AutoId-specific domain runtime config.
942#[derive(
943    TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
944)]
945pub struct AutoIdDomainRuntimeConfig {
946    // Currently, there is no specific configuration for AutoId.
947}
948
949/// Configurations for specific domain runtime kinds.
950#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
951pub enum DomainRuntimeConfig {
952    Evm(EvmDomainRuntimeConfig),
953    AutoId(AutoIdDomainRuntimeConfig),
954}
955
956impl Default for DomainRuntimeConfig {
957    fn default() -> Self {
958        Self::default_evm()
959    }
960}
961
962impl From<EvmDomainRuntimeConfig> for DomainRuntimeConfig {
963    fn from(evm_config: EvmDomainRuntimeConfig) -> Self {
964        DomainRuntimeConfig::Evm(evm_config)
965    }
966}
967
968impl From<AutoIdDomainRuntimeConfig> for DomainRuntimeConfig {
969    fn from(auto_id_config: AutoIdDomainRuntimeConfig) -> Self {
970        DomainRuntimeConfig::AutoId(auto_id_config)
971    }
972}
973
974impl DomainRuntimeConfig {
975    pub fn default_evm() -> Self {
976        DomainRuntimeConfig::Evm(EvmDomainRuntimeConfig::default())
977    }
978
979    pub fn default_auto_id() -> Self {
980        DomainRuntimeConfig::AutoId(AutoIdDomainRuntimeConfig::default())
981    }
982
983    pub fn is_evm_domain(&self) -> bool {
984        matches!(self, DomainRuntimeConfig::Evm(_))
985    }
986
987    pub fn is_auto_id(&self) -> bool {
988        matches!(self, DomainRuntimeConfig::AutoId(_))
989    }
990
991    pub fn evm(&self) -> Option<&EvmDomainRuntimeConfig> {
992        match self {
993            DomainRuntimeConfig::Evm(evm_config) => Some(evm_config),
994            _ => None,
995        }
996    }
997
998    pub fn initial_contract_creation_allow_list(
999        &self,
1000    ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
1001        self.evm()
1002            .and_then(|evm_config| evm_config.evm_type.initial_contract_creation_allow_list())
1003    }
1004
1005    pub fn auto_id(&self) -> Option<&AutoIdDomainRuntimeConfig> {
1006        match self {
1007            DomainRuntimeConfig::AutoId(auto_id_config) => Some(auto_id_config),
1008            _ => None,
1009        }
1010    }
1011}
1012
1013#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
1014pub struct GenesisDomain<AccountId: Ord, Balance> {
1015    // Domain runtime items
1016    pub runtime_name: String,
1017    pub runtime_type: RuntimeType,
1018    pub runtime_version: RuntimeVersion,
1019    pub raw_genesis_storage: Vec<u8>,
1020
1021    // Domain config items
1022    pub owner_account_id: AccountId,
1023    pub domain_name: String,
1024    pub bundle_slot_probability: (u64, u64),
1025    pub operator_allow_list: OperatorAllowList<AccountId>,
1026    /// Configurations for a specific type of domain runtime, for example, EVM.
1027    pub domain_runtime_config: DomainRuntimeConfig,
1028
1029    // Genesis operator
1030    pub signing_key: OperatorPublicKey,
1031    pub minimum_nominator_stake: Balance,
1032    pub nomination_tax: Percent,
1033
1034    // initial balances
1035    pub initial_balances: Vec<(MultiAccountId, Balance)>,
1036}
1037
1038/// Types of runtime pallet domains currently supports
1039#[derive(
1040    Debug, Default, Encode, Decode, TypeInfo, Copy, Clone, PartialEq, Eq, Serialize, Deserialize,
1041)]
1042pub enum RuntimeType {
1043    #[default]
1044    Evm,
1045    AutoId,
1046}
1047
1048/// Type representing the runtime ID.
1049pub type RuntimeId = u32;
1050
1051/// Type representing domain epoch.
1052pub type EpochIndex = u32;
1053
1054/// Type representing operator ID
1055pub type OperatorId = u64;
1056
1057/// Channel identity.
1058pub type ChannelId = sp_core::U256;
1059
1060/// Domains specific digest item.
1061#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
1062pub enum DomainDigestItem {
1063    DomainRuntimeUpgraded(RuntimeId),
1064    DomainInstantiated(DomainId),
1065}
1066
1067/// Domains specific digest items.
1068pub trait DomainsDigestItem {
1069    fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self;
1070    fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId>;
1071
1072    fn domain_instantiation(domain_id: DomainId) -> Self;
1073    fn as_domain_instantiation(&self) -> Option<DomainId>;
1074}
1075
1076impl DomainsDigestItem for DigestItem {
1077    fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self {
1078        Self::Other(DomainDigestItem::DomainRuntimeUpgraded(runtime_id).encode())
1079    }
1080
1081    fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId> {
1082        match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
1083            Some(DomainDigestItem::DomainRuntimeUpgraded(runtime_id)) => Some(runtime_id),
1084            _ => None,
1085        }
1086    }
1087
1088    fn domain_instantiation(domain_id: DomainId) -> Self {
1089        Self::Other(DomainDigestItem::DomainInstantiated(domain_id).encode())
1090    }
1091
1092    fn as_domain_instantiation(&self) -> Option<DomainId> {
1093        match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
1094            Some(DomainDigestItem::DomainInstantiated(domain_id)) => Some(domain_id),
1095            _ => None,
1096        }
1097    }
1098}
1099
1100/// EVM chain Id storage key.
1101///
1102/// This function should ideally use a Host function to fetch the storage key
1103/// from the domain runtime. But since the Host function is not available at Genesis, we have to
1104/// assume the storage keys.
1105/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
1106pub(crate) fn evm_chain_id_storage_key() -> StorageKey {
1107    StorageKey(
1108        storage_prefix(
1109            // This is the name used for `pallet_evm_chain_id` in the `construct_runtime` macro
1110            // i.e. `EVMChainId: pallet_evm_chain_id = 82,`
1111            "EVMChainId".as_bytes(),
1112            // This is the storage item name used inside `pallet_evm_chain_id`
1113            "ChainId".as_bytes(),
1114        )
1115        .to_vec(),
1116    )
1117}
1118
1119/// EVM contract creation allow list storage key.
1120///
1121/// This function should ideally use a Host function to fetch the storage key
1122/// from the domain runtime. But since the Host function is not available at Genesis, we have to
1123/// assume the storage keys.
1124/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
1125pub(crate) fn evm_contract_creation_allowed_by_storage_key() -> StorageKey {
1126    StorageKey(
1127        storage_prefix(
1128            // This is the name used for `pallet_evm_tracker` in the `construct_runtime` macro
1129            // i.e. `EVMNoncetracker: pallet_evm_tracker = 84,`
1130            "EVMNoncetracker".as_bytes(),
1131            // This is the storage item name used inside `pallet_evm_tracker`
1132            "ContractCreationAllowedBy".as_bytes(),
1133        )
1134        .to_vec(),
1135    )
1136}
1137
1138/// Total issuance storage key for Domains.
1139///
1140/// This function should ideally use a Host function to fetch the storage key
1141/// from the domain runtime. But since the Host function is not available at Genesis, we have to
1142/// assume the storage keys.
1143/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
1144pub fn domain_total_issuance_storage_key() -> StorageKey {
1145    StorageKey(
1146        storage_prefix(
1147            // This is the name used for `pallet_balances` in the `construct_runtime` macro
1148            "Balances".as_bytes(),
1149            // This is the storage item name used inside `pallet_balances`
1150            "TotalIssuance".as_bytes(),
1151        )
1152        .to_vec(),
1153    )
1154}
1155
1156/// Account info on frame_system on Domains
1157///
1158/// This function should ideally use a Host function to fetch the storage key
1159/// from the domain runtime. But since the Host function is not available at Genesis, we have to
1160/// assume the storage keys.
1161/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances.
1162pub fn domain_account_storage_key<AccountId: Encode>(who: AccountId) -> StorageKey {
1163    let storage_prefix = storage_prefix("System".as_bytes(), "Account".as_bytes());
1164    let key_hashed = who.using_encoded(Blake2_128Concat::hash);
1165
1166    let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
1167
1168    final_key.extend_from_slice(&storage_prefix);
1169    final_key.extend_from_slice(key_hashed.as_ref());
1170
1171    StorageKey(final_key)
1172}
1173
1174/// The storage key of the `SelfDomainId` storage item in `pallet-domain-id`
1175///
1176/// Any change to the storage item name or `pallet-domain-id` name used in the `construct_runtime`
1177/// macro must be reflected here.
1178pub fn self_domain_id_storage_key() -> StorageKey {
1179    StorageKey(
1180        frame_support::storage::storage_prefix(
1181            // This is the name used for `pallet-domain-id` in the `construct_runtime` macro
1182            // i.e. `SelfDomainId: pallet_domain_id = 90`
1183            "SelfDomainId".as_bytes(),
1184            // This is the storage item name used inside `pallet-domain-id`
1185            "SelfDomainId".as_bytes(),
1186        )
1187        .to_vec(),
1188    )
1189}
1190
1191/// `DomainInstanceData` is used to construct the genesis storage of domain instance chain
1192#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
1193pub struct DomainInstanceData {
1194    pub runtime_type: RuntimeType,
1195    pub raw_genesis: RawGenesis,
1196}
1197
1198#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
1199pub struct DomainBundleLimit {
1200    /// The max bundle size for the domain.
1201    pub max_bundle_size: u32,
1202    /// The max bundle weight for the domain.
1203    pub max_bundle_weight: Weight,
1204}
1205
1206/// Calculates the max bundle weight and size
1207// See https://forum.subspace.network/t/on-bundle-weight-limits-sum/2277 for more details
1208// about the formula
1209pub fn calculate_max_bundle_weight_and_size(
1210    max_domain_block_size: u32,
1211    max_domain_block_weight: Weight,
1212    consensus_slot_probability: (u64, u64),
1213    bundle_slot_probability: (u64, u64),
1214) -> Option<DomainBundleLimit> {
1215    // (n1 / d1) / (n2 / d2) is equal to (n1 * d2) / (d1 * n2)
1216    // This represents: bundle_slot_probability/SLOT_PROBABILITY
1217    let expected_bundles_per_block = bundle_slot_probability
1218        .0
1219        .checked_mul(consensus_slot_probability.1)?
1220        .checked_div(
1221            bundle_slot_probability
1222                .1
1223                .checked_mul(consensus_slot_probability.0)?,
1224        )?;
1225
1226    // set the proof size for bundle to be proof size of max domain weight
1227    // so that each domain extrinsic can use the full proof size if required
1228    let max_proof_size = max_domain_block_weight.proof_size();
1229    let max_bundle_weight = max_domain_block_weight
1230        .checked_div(expected_bundles_per_block)?
1231        .set_proof_size(max_proof_size);
1232
1233    let max_bundle_size =
1234        (max_domain_block_size as u64).checked_div(expected_bundles_per_block)? as u32;
1235
1236    Some(DomainBundleLimit {
1237        max_bundle_size,
1238        max_bundle_weight,
1239    })
1240}
1241
1242/// Checks if the signer Id hash is within the tx range
1243pub fn signer_in_tx_range(bundle_vrf_hash: &U256, signer_id_hash: &U256, tx_range: &U256) -> bool {
1244    let distance_from_vrf_hash = bidirectional_distance(bundle_vrf_hash, signer_id_hash);
1245    distance_from_vrf_hash <= (*tx_range / 2)
1246}
1247
1248/// Receipt invalidity type.
1249#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
1250pub enum InvalidReceipt {
1251    /// The field `invalid_bundles` in [`ExecutionReceipt`] is invalid.
1252    InvalidBundles,
1253}
1254
1255#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
1256pub enum ReceiptValidity {
1257    Valid,
1258    Invalid(InvalidReceipt),
1259}
1260
1261/// Bundle invalidity type
1262///
1263/// Each type contains the index of the first invalid extrinsic within the bundle
1264#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
1265pub enum InvalidBundleType {
1266    /// Failed to decode the opaque extrinsic.
1267    #[codec(index = 0)]
1268    UndecodableTx(u32),
1269    /// Transaction is out of the tx range.
1270    #[codec(index = 1)]
1271    OutOfRangeTx(u32),
1272    /// Transaction is illegal (unable to pay the fee, etc).
1273    #[codec(index = 2)]
1274    IllegalTx(u32),
1275    /// Transaction is an invalid XDM.
1276    #[codec(index = 3)]
1277    InvalidXDM(u32),
1278    /// Transaction is an inherent extrinsic.
1279    #[codec(index = 4)]
1280    InherentExtrinsic(u32),
1281    /// The `estimated_bundle_weight` in the bundle header is invalid
1282    #[codec(index = 5)]
1283    InvalidBundleWeight,
1284}
1285
1286impl InvalidBundleType {
1287    // Return the checking order of the invalid type
1288    pub fn checking_order(&self) -> u64 {
1289        // A bundle can contains multiple invalid extrinsics thus consider the first invalid extrinsic
1290        // as the invalid type
1291        let extrinsic_order = match self {
1292            Self::UndecodableTx(i) => *i,
1293            Self::OutOfRangeTx(i) => *i,
1294            Self::IllegalTx(i) => *i,
1295            Self::InvalidXDM(i) => *i,
1296            Self::InherentExtrinsic(i) => *i,
1297            // NOTE: the `InvalidBundleWeight` is targeting the whole bundle not a specific
1298            // single extrinsic, as `extrinsic_index` is used as the order to check the extrinsic
1299            // in the bundle returning `u32::MAX` indicate `InvalidBundleWeight` is checked after
1300            // all the extrinsic in the bundle is checked.
1301            Self::InvalidBundleWeight => u32::MAX,
1302        };
1303
1304        // The extrinsic can be considered as invalid due to multiple `invalid_type` (i.e. an extrinsic
1305        // can be `OutOfRangeTx` and `IllegalTx` at the same time) thus use the following checking order
1306        // and consider the first check as the invalid type
1307        //
1308        // NOTE: Use explicit number as the order instead of the enum discriminant
1309        // to avoid changing the order accidentally
1310        let rule_order = match self {
1311            Self::UndecodableTx(_) => 1,
1312            Self::OutOfRangeTx(_) => 2,
1313            Self::InherentExtrinsic(_) => 3,
1314            Self::InvalidXDM(_) => 4,
1315            Self::IllegalTx(_) => 5,
1316            Self::InvalidBundleWeight => 6,
1317        };
1318
1319        // The checking order is a combination of the `extrinsic_order` and `rule_order`
1320        // it presents as an `u64` where the first 32 bits is the `extrinsic_order` and
1321        // last 32 bits is the `rule_order` meaning the `extrinsic_order` is checked first
1322        // then the `rule_order`.
1323        ((extrinsic_order as u64) << 32) | (rule_order as u64)
1324    }
1325
1326    // Return the index of the extrinsic that the invalid type points to
1327    //
1328    // NOTE: `InvalidBundleWeight` will return `None` since it is targeting the whole bundle not a
1329    // specific single extrinsic
1330    pub fn extrinsic_index(&self) -> Option<u32> {
1331        match self {
1332            Self::UndecodableTx(i) => Some(*i),
1333            Self::OutOfRangeTx(i) => Some(*i),
1334            Self::IllegalTx(i) => Some(*i),
1335            Self::InvalidXDM(i) => Some(*i),
1336            Self::InherentExtrinsic(i) => Some(*i),
1337            Self::InvalidBundleWeight => None,
1338        }
1339    }
1340}
1341
1342#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
1343pub enum BundleValidity<Hash> {
1344    // The invalid bundle was originally included in the consensus block but subsequently
1345    // excluded from execution as invalid and holds the `InvalidBundleType`
1346    Invalid(InvalidBundleType),
1347    // The valid bundle's hash of `Vec<(tx_signer, tx_hash)>` of all domain extrinsic being
1348    // included in the bundle.
1349    // TODO remove this and use host function to fetch above mentioned data
1350    Valid(Hash),
1351}
1352
1353/// [`InboxedBundle`] represents a bundle that was successfully submitted to the consensus chain
1354#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
1355pub struct InboxedBundle<Hash> {
1356    pub bundle: BundleValidity<Hash>,
1357    // TODO remove this as the root is already present in the `ExecutionInbox` storage
1358    pub extrinsics_root: Hash,
1359}
1360
1361impl<Hash> InboxedBundle<Hash> {
1362    pub fn valid(bundle_digest_hash: Hash, extrinsics_root: Hash) -> Self {
1363        InboxedBundle {
1364            bundle: BundleValidity::Valid(bundle_digest_hash),
1365            extrinsics_root,
1366        }
1367    }
1368
1369    pub fn invalid(invalid_bundle_type: InvalidBundleType, extrinsics_root: Hash) -> Self {
1370        InboxedBundle {
1371            bundle: BundleValidity::Invalid(invalid_bundle_type),
1372            extrinsics_root,
1373        }
1374    }
1375
1376    pub fn is_invalid(&self) -> bool {
1377        matches!(self.bundle, BundleValidity::Invalid(_))
1378    }
1379
1380    pub fn invalid_extrinsic_index(&self) -> Option<u32> {
1381        match &self.bundle {
1382            BundleValidity::Invalid(invalid_bundle_type) => invalid_bundle_type.extrinsic_index(),
1383            BundleValidity::Valid(_) => None,
1384        }
1385    }
1386
1387    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
1388    pub fn dummy(extrinsics_root: Hash) -> Self
1389    where
1390        Hash: Default,
1391    {
1392        InboxedBundle {
1393            bundle: BundleValidity::Valid(Hash::default()),
1394            extrinsics_root,
1395        }
1396    }
1397}
1398
1399/// Empty extrinsics root.
1400pub const EMPTY_EXTRINSIC_ROOT: ExtrinsicsRoot = ExtrinsicsRoot {
1401    0: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"),
1402};
1403
1404pub fn derive_domain_block_hash<DomainHeader: HeaderT>(
1405    domain_block_number: DomainHeader::Number,
1406    extrinsics_root: DomainHeader::Hash,
1407    state_root: DomainHeader::Hash,
1408    parent_domain_block_hash: DomainHeader::Hash,
1409    digest: Digest,
1410) -> DomainHeader::Hash {
1411    let domain_header = DomainHeader::new(
1412        domain_block_number,
1413        extrinsics_root,
1414        state_root,
1415        parent_domain_block_hash,
1416        digest,
1417    );
1418
1419    domain_header.hash()
1420}
1421
1422/// Represents the extrinsic either as full data or hash of the data.
1423#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
1424pub enum ExtrinsicDigest {
1425    /// Actual extrinsic data that is inlined since it is less than 33 bytes.
1426    Data(Vec<u8>),
1427    /// Extrinsic Hash.
1428    Hash(H256),
1429}
1430
1431impl ExtrinsicDigest {
1432    pub fn new<Layout: TrieLayout>(ext: Vec<u8>) -> Self
1433    where
1434        Layout::Hash: HashT,
1435        <Layout::Hash as HashT>::Output: Into<H256>,
1436    {
1437        if let Some(threshold) = Layout::MAX_INLINE_VALUE {
1438            if ext.len() >= threshold as usize {
1439                ExtrinsicDigest::Hash(Layout::Hash::hash(&ext).into())
1440            } else {
1441                ExtrinsicDigest::Data(ext)
1442            }
1443        } else {
1444            ExtrinsicDigest::Data(ext)
1445        }
1446    }
1447}
1448
1449/// Trait that tracks the balances on Domains.
1450pub trait DomainsTransfersTracker<Balance> {
1451    type Error;
1452
1453    /// Initializes the domain balance
1454    fn initialize_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
1455
1456    /// Notes a transfer between chains.
1457    /// Balance on from_chain_id is reduced if it is a domain chain
1458    fn note_transfer(
1459        from_chain_id: ChainId,
1460        to_chain_id: ChainId,
1461        amount: Balance,
1462    ) -> Result<(), Self::Error>;
1463
1464    /// Confirms a transfer between chains.
1465    fn confirm_transfer(
1466        from_chain_id: ChainId,
1467        to_chain_id: ChainId,
1468        amount: Balance,
1469    ) -> Result<(), Self::Error>;
1470
1471    /// Claims a rejected transfer between chains.
1472    fn claim_rejected_transfer(
1473        from_chain_id: ChainId,
1474        to_chain_id: ChainId,
1475        amount: Balance,
1476    ) -> Result<(), Self::Error>;
1477
1478    /// Rejects a initiated transfer between chains.
1479    fn reject_transfer(
1480        from_chain_id: ChainId,
1481        to_chain_id: ChainId,
1482        amount: Balance,
1483    ) -> Result<(), Self::Error>;
1484
1485    /// Reduces a given amount from the domain balance
1486    fn reduce_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
1487}
1488
1489/// Trait to check domain owner.
1490pub trait DomainOwner<AccountId> {
1491    /// Returns true if the account is the domain owner.
1492    fn is_domain_owner(domain_id: DomainId, acc: AccountId) -> bool;
1493}
1494
1495impl<AccountId> DomainOwner<AccountId> for () {
1496    fn is_domain_owner(_domain_id: DomainId, _acc: AccountId) -> bool {
1497        false
1498    }
1499}
1500
1501/// Post hook to know if the domain had bundle submitted in the previous block.
1502pub trait DomainBundleSubmitted {
1503    /// Called in the next block initialisation if there was a domain bundle in the previous block.
1504    /// This hook if called for domain represents that there is a new domain block for parent consensus block.
1505    fn domain_bundle_submitted(domain_id: DomainId);
1506}
1507
1508impl DomainBundleSubmitted for () {
1509    fn domain_bundle_submitted(_domain_id: DomainId) {}
1510}
1511
1512/// A hook to call after a domain is instantiated
1513pub trait OnDomainInstantiated {
1514    fn on_domain_instantiated(domain_id: DomainId);
1515}
1516
1517impl OnDomainInstantiated for () {
1518    fn on_domain_instantiated(_domain_id: DomainId) {}
1519}
1520
1521pub type ExecutionReceiptFor<DomainHeader, CBlock, Balance> = ExecutionReceipt<
1522    NumberFor<CBlock>,
1523    BlockHashFor<CBlock>,
1524    <DomainHeader as HeaderT>::Number,
1525    <DomainHeader as HeaderT>::Hash,
1526    Balance,
1527>;
1528
1529/// Domain chains allowlist updates.
1530#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
1531pub struct DomainAllowlistUpdates {
1532    /// Chains that are allowed to open a channel with this chain.
1533    pub allow_chains: BTreeSet<ChainId>,
1534    /// Chains that are not allowed to open a channel with this chain.
1535    pub remove_chains: BTreeSet<ChainId>,
1536}
1537
1538impl DomainAllowlistUpdates {
1539    pub fn is_empty(&self) -> bool {
1540        self.allow_chains.is_empty() && self.remove_chains.is_empty()
1541    }
1542
1543    pub fn clear(&mut self) {
1544        self.allow_chains.clear();
1545        self.remove_chains.clear();
1546    }
1547}
1548
1549/// Domain Sudo runtime call.
1550///
1551/// This structure exists because we need to generate a storage proof for FP
1552/// and Storage shouldn't be None. So each domain must always hold this value even if
1553/// there is an empty runtime call inside
1554#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
1555pub struct DomainSudoCall {
1556    pub maybe_call: Option<Vec<u8>>,
1557}
1558
1559impl DomainSudoCall {
1560    pub fn clear(&mut self) {
1561        self.maybe_call.take();
1562    }
1563}
1564
1565/// EVM Domain "update contract creation allowed by" runtime call.
1566///
1567/// This structure exists because we need to generate a storage proof for FP
1568/// and Storage shouldn't be None. So each domain must always hold this value even if
1569/// there is an empty runtime call inside
1570#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
1571pub struct EvmDomainContractCreationAllowedByCall {
1572    pub maybe_call: Option<PermissionedActionAllowedBy<EthereumAccountId>>,
1573}
1574
1575impl EvmDomainContractCreationAllowedByCall {
1576    pub fn clear(&mut self) {
1577        self.maybe_call.take();
1578    }
1579}
1580
1581#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
1582pub struct RuntimeObject<Number, Hash> {
1583    pub runtime_name: String,
1584    pub runtime_type: RuntimeType,
1585    pub runtime_upgrades: u32,
1586    pub instance_count: u32,
1587    pub hash: Hash,
1588    // The raw genesis storage that contains the runtime code.
1589    // NOTE: don't use this field directly but `into_complete_raw_genesis` instead
1590    pub raw_genesis: RawGenesis,
1591    pub version: RuntimeVersion,
1592    pub created_at: Number,
1593    pub updated_at: Number,
1594}
1595
1596/// Digest storage key in frame_system.
1597/// Unfortunately, the digest storage is private and not possible to derive the key from it directly.
1598pub fn system_digest_final_key() -> Vec<u8> {
1599    frame_support::storage::storage_prefix("System".as_ref(), "Digest".as_ref()).to_vec()
1600}
1601
1602/// Hook to handle chain rewards.
1603pub trait OnChainRewards<Balance> {
1604    fn on_chain_rewards(chain_id: ChainId, reward: Balance);
1605}
1606
1607impl<Balance> OnChainRewards<Balance> for () {
1608    fn on_chain_rewards(_chain_id: ChainId, _reward: Balance) {}
1609}
1610
1611#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
1612pub enum OperatorRewardSource<Number> {
1613    Bundle {
1614        at_block_number: Number,
1615    },
1616    XDMProtocolFees,
1617    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
1618    Dummy,
1619}
1620
1621pub trait SkipBalanceChecks {
1622    fn should_skip_balance_check(chain_id: ChainId) -> bool;
1623}
1624
1625impl SkipBalanceChecks for () {
1626    fn should_skip_balance_check(_chain_id: ChainId) -> bool {
1627        false
1628    }
1629}
1630
1631sp_api::decl_runtime_apis! {
1632    /// APIs used to access the domains pallet.
1633    // When updating this version, document new APIs with "Only present in API versions" comments.
1634    // TODO: when removing this version, also remove "Only present in API versions" comments and
1635    // deprecated attributes.
1636    #[api_version(4)]
1637    pub trait DomainsApi<DomainHeader: HeaderT> {
1638        /// Submits the transaction bundle via an unsigned extrinsic.
1639        fn submit_bundle_unsigned(opaque_bundle: OpaqueBundle<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1640
1641        // Submits a singleton receipt via an unsigned extrinsic.
1642        fn submit_receipt_unsigned(singleton_receipt: SealedSingletonReceipt<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1643
1644        /// Extracts the bundles successfully stored from the given extrinsics.
1645        fn extract_successful_bundles(
1646            domain_id: DomainId,
1647            extrinsics: Vec<Block::Extrinsic>,
1648        ) -> OpaqueBundles<Block, DomainHeader, Balance>;
1649
1650        /// Generates a randomness seed for extrinsics shuffling.
1651        fn extrinsics_shuffling_seed() -> Randomness;
1652
1653        /// Returns the current WASM bundle for the given `domain_id`.
1654        fn domain_runtime_code(domain_id: DomainId) -> Option<Vec<u8>>;
1655
1656        /// Returns the runtime id for the given `domain_id`.
1657        fn runtime_id(domain_id: DomainId) -> Option<RuntimeId>;
1658
1659        /// Returns the list of runtime upgrades in the current block.
1660        /// Only present in API versions 2 and later.
1661        fn runtime_upgrades() -> Vec<RuntimeId>;
1662
1663        /// Returns the domain instance data for the given `domain_id`.
1664        fn domain_instance_data(domain_id: DomainId) -> Option<(DomainInstanceData, NumberFor<Block>)>;
1665
1666        /// Returns the current timestamp at the current height.
1667        fn domain_timestamp() -> Moment;
1668
1669        /// Returns the current timestamp at the current height.
1670        #[allow(clippy::deprecated_semver)]
1671        #[deprecated(since = "3", note = "Use `domain_timestamp()` instead")]
1672        fn timestamp() -> Moment;
1673
1674        /// Returns the consensus transaction byte fee that will used to charge the domain
1675        /// transaction for consensus chain storage fees.
1676        fn consensus_transaction_byte_fee() -> Balance;
1677
1678        /// Returns the consensus chain byte fee that will used to charge the domain transaction
1679        /// for consensus chain storage fees.
1680        #[allow(clippy::deprecated_semver)]
1681        #[deprecated(since = "3", note = "Use `consensus_transaction_byte_fee()` instead")]
1682        fn consensus_chain_byte_fee() -> Balance;
1683
1684        /// Returns the current Tx range for the given domain Id.
1685        fn domain_tx_range(domain_id: DomainId) -> U256;
1686
1687        /// Returns the genesis state root if not pruned.
1688        fn genesis_state_root(domain_id: DomainId) -> Option<H256>;
1689
1690        /// Returns the best execution chain number.
1691        fn head_receipt_number(domain_id: DomainId) -> HeaderNumberFor<DomainHeader>;
1692
1693        /// Returns the block number of oldest unconfirmed execution receipt.
1694        fn oldest_unconfirmed_receipt_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1695
1696        /// Returns the domain bundle limit of the given domain.
1697        fn domain_bundle_limit(domain_id: DomainId) -> Option<DomainBundleLimit>;
1698
1699        /// Returns true if there are any ERs in the challenge period with non empty extrinsics.
1700        fn non_empty_er_exists(domain_id: DomainId) -> bool;
1701
1702        /// Returns the current best block number for the domain.
1703        fn domain_best_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1704
1705        /// Returns the execution receipt with the given hash.
1706        fn execution_receipt(receipt_hash: HeaderHashFor<DomainHeader>) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1707
1708        /// Returns the current epoch and the next epoch operators of the given domain.
1709        fn domain_operators(domain_id: DomainId) -> Option<(BTreeMap<OperatorId, Balance>, Vec<OperatorId>)>;
1710
1711        /// Returns the execution receipt hash of the given domain and domain block number.
1712        fn receipt_hash(domain_id: DomainId, domain_number: HeaderNumberFor<DomainHeader>) -> Option<HeaderHashFor<DomainHeader>>;
1713
1714        /// Returns the latest confirmed domain block number and hash.
1715        fn latest_confirmed_domain_block(domain_id: DomainId) -> Option<(HeaderNumberFor<DomainHeader>, HeaderHashFor<DomainHeader>)>;
1716
1717        /// Returns true if the receipt exists and is going to be pruned
1718        fn is_bad_er_pending_to_prune(domain_id: DomainId, receipt_hash: HeaderHashFor<DomainHeader>) -> bool;
1719
1720        /// Returns the balance of the storage fund account.
1721        fn storage_fund_account_balance(operator_id: OperatorId) -> Balance;
1722
1723        /// Returns true if the given domain's runtime code has been upgraded since `at`.
1724        fn is_domain_runtime_upgraded_since(domain_id: DomainId, at: NumberFor<Block>) -> Option<bool>;
1725
1726        /// Returns the domain sudo call for the given domain, if any.
1727        fn domain_sudo_call(domain_id: DomainId) -> Option<Vec<u8>>;
1728
1729        /// Returns the "set contract creation allowed by" call for the given EVM domain, if any.
1730        /// Only present in API versions 4 and later.
1731        fn evm_domain_contract_creation_allowed_by_call(domain_id: DomainId) -> Option<PermissionedActionAllowedBy<EthereumAccountId>>;
1732
1733        /// Returns the last confirmed domain block execution receipt.
1734        fn last_confirmed_domain_block_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1735    }
1736
1737    pub trait BundleProducerElectionApi<Balance: Encode + Decode> {
1738        fn bundle_producer_election_params(domain_id: DomainId) -> Option<BundleProducerElectionParams<Balance>>;
1739
1740        fn operator(operator_id: OperatorId) -> Option<(OperatorPublicKey, Balance)>;
1741    }
1742}