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