1#![cfg_attr(not(feature = "std"), no_std)]
4
5pub mod bundle;
6pub mod bundle_producer_election;
7pub mod core_api;
8pub mod execution_receipt;
9pub mod extrinsics;
10pub mod merkle_tree;
11pub mod proof_provider_and_verifier;
12pub mod storage;
13#[cfg(test)]
14mod tests;
15pub mod valued_trie;
16
17#[cfg(not(feature = "std"))]
18extern crate alloc;
19
20use crate::bundle::{BundleVersion, OpaqueBundle, OpaqueBundles};
21use crate::execution_receipt::ExecutionReceiptVersion;
22use crate::storage::{RawGenesis, StorageKey};
23#[cfg(not(feature = "std"))]
24use alloc::collections::BTreeSet;
25#[cfg(not(feature = "std"))]
26use alloc::string::String;
27#[cfg(not(feature = "std"))]
28use alloc::vec::Vec;
29use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionError};
30use core::num::ParseIntError;
31use core::ops::{Add, Sub};
32use core::str::FromStr;
33use domain_runtime_primitives::{EVMChainId, EthereumAccountId, MultiAccountId};
34use execution_receipt::{ExecutionReceiptFor, SealedSingletonReceipt};
35use frame_support::storage::storage_prefix;
36use frame_support::{Blake2_128Concat, StorageHasher};
37use hex_literal::hex;
38use parity_scale_codec::{Codec, Decode, Encode, MaxEncodedLen};
39use scale_info::TypeInfo;
40use serde::{Deserialize, Serialize};
41use sp_core::H256;
42use sp_core::crypto::KeyTypeId;
43use sp_core::sr25519::vrf::VrfSignature;
44#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
45use sp_core::sr25519::vrf::{VrfPreOutput, VrfProof};
46use sp_runtime::generic::OpaqueDigestItemId;
47use sp_runtime::traits::{CheckedAdd, Hash as HashT, Header as HeaderT, NumberFor};
48use sp_runtime::{Digest, DigestItem, Percent};
49use sp_runtime_interface::pass_by;
50use sp_runtime_interface::pass_by::PassBy;
51use sp_std::collections::btree_map::BTreeMap;
52use sp_std::fmt::{Display, Formatter};
53use sp_trie::TrieLayout;
54use sp_version::RuntimeVersion;
55use sp_weights::Weight;
56#[cfg(feature = "std")]
57use std::collections::BTreeSet;
58use subspace_core_primitives::hashes::{Blake3Hash, blake3_hash};
59use subspace_core_primitives::pot::PotOutput;
60use subspace_core_primitives::solutions::bidirectional_distance;
61use subspace_core_primitives::{Randomness, U256};
62use subspace_runtime_primitives::{Balance, Moment};
63
64pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"oper");
66
67pub const DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT: &[u8] = b"extrinsics-shuffling-seed";
69
70mod app {
71 use super::KEY_TYPE;
72 use sp_application_crypto::{app_crypto, sr25519};
73
74 app_crypto!(sr25519, KEY_TYPE);
75}
76
77pub const DOMAIN_STORAGE_FEE_MULTIPLIER: Balance = 3;
84
85pub type OperatorSignature = app::Signature;
87
88#[cfg(feature = "std")]
91pub type OperatorPair = app::Pair;
92
93pub type OperatorPublicKey = app::Public;
95
96pub struct OperatorKey;
98
99impl sp_runtime::BoundToRuntimeAppPublic for OperatorKey {
100 type Public = OperatorPublicKey;
101}
102
103pub type StakeWeight = u128;
107
108pub type ExtrinsicsRoot = H256;
110
111pub type HeaderHashingFor<Header> = <Header as HeaderT>::Hashing;
113pub type HeaderNumberFor<Header> = <Header as HeaderT>::Number;
115pub type HeaderHashFor<Header> = <Header as HeaderT>::Hash;
117
118#[derive(
120 Clone,
121 Copy,
122 Debug,
123 Hash,
124 Default,
125 Eq,
126 PartialEq,
127 Ord,
128 PartialOrd,
129 Encode,
130 Decode,
131 TypeInfo,
132 Serialize,
133 Deserialize,
134 MaxEncodedLen,
135)]
136pub struct DomainId(u32);
137
138impl From<u32> for DomainId {
139 #[inline]
140 fn from(x: u32) -> Self {
141 Self(x)
142 }
143}
144
145impl From<DomainId> for u32 {
146 #[inline]
147 fn from(domain_id: DomainId) -> Self {
148 domain_id.0
149 }
150}
151
152impl FromStr for DomainId {
153 type Err = ParseIntError;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 s.parse::<u32>().map(Into::into)
157 }
158}
159
160impl Add<DomainId> for DomainId {
161 type Output = Self;
162
163 fn add(self, other: DomainId) -> Self {
164 Self(self.0 + other.0)
165 }
166}
167
168impl Sub<DomainId> for DomainId {
169 type Output = Self;
170
171 fn sub(self, other: DomainId) -> Self {
172 Self(self.0 - other.0)
173 }
174}
175
176impl CheckedAdd for DomainId {
177 fn checked_add(&self, rhs: &Self) -> Option<Self> {
178 self.0.checked_add(rhs.0).map(Self)
179 }
180}
181
182impl DomainId {
183 pub const fn new(id: u32) -> Self {
185 Self(id)
186 }
187
188 pub fn to_le_bytes(&self) -> [u8; 4] {
190 self.0.to_le_bytes()
191 }
192}
193
194impl Display for DomainId {
195 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
196 self.0.fmt(f)
197 }
198}
199
200impl PassBy for DomainId {
201 type PassBy = pass_by::Codec<Self>;
202}
203
204#[derive(
206 Clone,
207 Copy,
208 Debug,
209 Hash,
210 Eq,
211 PartialEq,
212 Ord,
213 PartialOrd,
214 Encode,
215 Decode,
216 TypeInfo,
217 Serialize,
218 Deserialize,
219 MaxEncodedLen,
220)]
221pub enum ChainId {
222 Consensus,
223 Domain(DomainId),
224}
225
226impl ChainId {
227 #[inline]
228 pub fn consensus_chain_id() -> Self {
229 Self::Consensus
230 }
231
232 #[inline]
233 pub fn is_consensus_chain(&self) -> bool {
234 match self {
235 ChainId::Consensus => true,
236 ChainId::Domain(_) => false,
237 }
238 }
239
240 #[inline]
241 pub fn maybe_domain_chain(&self) -> Option<DomainId> {
242 match self {
243 ChainId::Consensus => None,
244 ChainId::Domain(domain_id) => Some(*domain_id),
245 }
246 }
247}
248
249impl From<u32> for ChainId {
250 #[inline]
251 fn from(x: u32) -> Self {
252 Self::Domain(DomainId::new(x))
253 }
254}
255
256impl From<DomainId> for ChainId {
257 #[inline]
258 fn from(x: DomainId) -> Self {
259 Self::Domain(x)
260 }
261}
262
263pub const INITIAL_DOMAIN_TX_RANGE: u64 = 3;
268
269#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
270pub struct ProofOfElection {
271 pub domain_id: DomainId,
273 pub slot_number: u64,
275 pub proof_of_time: PotOutput,
277 pub vrf_signature: VrfSignature,
279 pub operator_id: OperatorId,
281}
282
283impl ProofOfElection {
284 pub fn verify_vrf_signature(
285 &self,
286 operator_signing_key: &OperatorPublicKey,
287 ) -> Result<(), ProofOfElectionError> {
288 let global_challenge = self
289 .proof_of_time
290 .derive_global_randomness()
291 .derive_global_challenge(self.slot_number);
292 bundle_producer_election::verify_vrf_signature(
293 self.domain_id,
294 operator_signing_key,
295 &self.vrf_signature,
296 &global_challenge,
297 )
298 }
299
300 pub fn vrf_hash(&self) -> Blake3Hash {
302 let mut bytes = self.vrf_signature.pre_output.encode();
303 bytes.append(&mut self.vrf_signature.proof.encode());
304 blake3_hash(&bytes)
305 }
306
307 pub fn slot_number(&self) -> u64 {
308 self.slot_number
309 }
310}
311
312impl ProofOfElection {
313 #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
314 pub fn dummy(domain_id: DomainId, operator_id: OperatorId) -> Self {
315 let output_bytes = sp_std::vec![0u8; VrfPreOutput::max_encoded_len()];
316 let proof_bytes = sp_std::vec![0u8; VrfProof::max_encoded_len()];
317 let vrf_signature = VrfSignature {
318 pre_output: VrfPreOutput::decode(&mut output_bytes.as_slice()).unwrap(),
319 proof: VrfProof::decode(&mut proof_bytes.as_slice()).unwrap(),
320 };
321 Self {
322 domain_id,
323 slot_number: 0u64,
324 proof_of_time: PotOutput::default(),
325 vrf_signature,
326 operator_id,
327 }
328 }
329}
330
331#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
333pub enum OperatorAllowList<AccountId: Ord> {
334 Anyone,
336 Operators(BTreeSet<AccountId>),
339}
340
341impl<AccountId: Ord> OperatorAllowList<AccountId> {
342 pub fn is_operator_allowed(&self, operator: &AccountId) -> bool {
344 match self {
345 OperatorAllowList::Anyone => true,
346 OperatorAllowList::Operators(allowed_operators) => allowed_operators.contains(operator),
347 }
348 }
349}
350
351#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
353pub enum PermissionedActionAllowedBy<AccountId: Codec + Clone> {
354 Accounts(Vec<AccountId>),
355 Anyone,
356}
357
358impl<AccountId: Codec + PartialEq + Clone> PermissionedActionAllowedBy<AccountId> {
359 pub fn is_allowed(&self, who: &AccountId) -> bool {
360 match self {
361 PermissionedActionAllowedBy::Accounts(accounts) => accounts.contains(who),
362 PermissionedActionAllowedBy::Anyone => true,
363 }
364 }
365
366 pub fn is_anyone_allowed(&self) -> bool {
367 matches!(self, PermissionedActionAllowedBy::Anyone)
368 }
369}
370
371#[derive(
373 TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
374)]
375pub enum EvmType {
376 #[default]
377 Public,
379 Private {
381 initial_contract_creation_allow_list: PermissionedActionAllowedBy<EthereumAccountId>,
384 },
385}
386
387impl EvmType {
388 pub fn initial_contract_creation_allow_list(
390 &self,
391 ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
392 match self {
393 EvmType::Public => None,
394 EvmType::Private {
395 initial_contract_creation_allow_list,
396 } => Some(initial_contract_creation_allow_list),
397 }
398 }
399
400 pub fn is_public_evm_domain(&self) -> bool {
402 matches!(self, EvmType::Public)
403 }
404
405 pub fn is_private_evm_domain(&self) -> bool {
407 matches!(self, EvmType::Private { .. })
408 }
409}
410
411#[derive(
413 TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
414)]
415pub struct EvmDomainRuntimeConfig {
416 pub evm_type: EvmType,
417}
418
419#[derive(
421 TypeInfo, Debug, Default, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize,
422)]
423pub struct AutoIdDomainRuntimeConfig {
424 }
426
427#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
429pub enum DomainRuntimeInfo {
430 Evm {
431 chain_id: EVMChainId,
433 domain_runtime_config: EvmDomainRuntimeConfig,
435 },
436 AutoId {
437 domain_runtime_config: AutoIdDomainRuntimeConfig,
439 },
440}
441
442impl From<(EVMChainId, EvmDomainRuntimeConfig)> for DomainRuntimeInfo {
443 fn from(v: (EVMChainId, EvmDomainRuntimeConfig)) -> Self {
444 DomainRuntimeInfo::Evm {
445 chain_id: v.0,
446 domain_runtime_config: v.1,
447 }
448 }
449}
450
451impl From<AutoIdDomainRuntimeConfig> for DomainRuntimeInfo {
452 fn from(auto_id_config: AutoIdDomainRuntimeConfig) -> Self {
453 DomainRuntimeInfo::AutoId {
454 domain_runtime_config: auto_id_config,
455 }
456 }
457}
458
459impl DomainRuntimeInfo {
460 pub fn evm(&self) -> Option<&EvmDomainRuntimeConfig> {
461 match self {
462 DomainRuntimeInfo::Evm {
463 domain_runtime_config,
464 ..
465 } => Some(domain_runtime_config),
466 _ => None,
467 }
468 }
469
470 pub fn initial_contract_creation_allow_list(
471 &self,
472 ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
473 self.evm()
474 .and_then(|evm_config| evm_config.evm_type.initial_contract_creation_allow_list())
475 }
476
477 pub fn auto_id(&self) -> Option<&AutoIdDomainRuntimeConfig> {
478 match self {
479 DomainRuntimeInfo::AutoId {
480 domain_runtime_config,
481 } => Some(domain_runtime_config),
482 _ => None,
483 }
484 }
485
486 pub fn evm_chain_id(&self) -> Option<EVMChainId> {
488 match self {
489 Self::Evm { chain_id, .. } => Some(*chain_id),
490 _ => None,
491 }
492 }
493
494 pub fn is_evm_domain(&self) -> bool {
495 matches!(self, Self::Evm { .. })
496 }
497
498 pub fn is_private_evm_domain(&self) -> bool {
501 if let Self::Evm {
502 domain_runtime_config,
503 ..
504 } = self
505 {
506 domain_runtime_config.evm_type.is_private_evm_domain()
507 } else {
508 false
509 }
510 }
511
512 pub fn is_auto_id(&self) -> bool {
513 matches!(self, Self::AutoId { .. })
514 }
515}
516
517#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
518pub struct GenesisDomain<AccountId: Ord, Balance> {
519 pub runtime_name: String,
521 pub runtime_type: RuntimeType,
522 pub runtime_version: RuntimeVersion,
523 pub raw_genesis_storage: Vec<u8>,
524
525 pub owner_account_id: AccountId,
527 pub domain_name: String,
528 pub bundle_slot_probability: (u64, u64),
529 pub operator_allow_list: OperatorAllowList<AccountId>,
530 pub domain_runtime_info: DomainRuntimeInfo,
532
533 pub signing_key: OperatorPublicKey,
535 pub minimum_nominator_stake: Balance,
536 pub nomination_tax: Percent,
537
538 pub initial_balances: Vec<(MultiAccountId, Balance)>,
540}
541
542#[derive(
544 Debug, Default, Encode, Decode, TypeInfo, Copy, Clone, PartialEq, Eq, Serialize, Deserialize,
545)]
546pub enum RuntimeType {
547 #[default]
548 Evm,
549 AutoId,
550}
551
552pub type RuntimeId = u32;
554
555pub type EpochIndex = u32;
557
558pub type OperatorId = u64;
560
561pub type ChannelId = sp_core::U256;
563
564#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
566pub enum DomainDigestItem {
567 DomainRuntimeUpgraded(RuntimeId),
568 DomainInstantiated(DomainId),
569}
570
571pub trait DomainsDigestItem {
573 fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self;
574 fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId>;
575
576 fn domain_instantiation(domain_id: DomainId) -> Self;
577 fn as_domain_instantiation(&self) -> Option<DomainId>;
578}
579
580impl DomainsDigestItem for DigestItem {
581 fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self {
582 Self::Other(DomainDigestItem::DomainRuntimeUpgraded(runtime_id).encode())
583 }
584
585 fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId> {
586 match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
587 Some(DomainDigestItem::DomainRuntimeUpgraded(runtime_id)) => Some(runtime_id),
588 _ => None,
589 }
590 }
591
592 fn domain_instantiation(domain_id: DomainId) -> Self {
593 Self::Other(DomainDigestItem::DomainInstantiated(domain_id).encode())
594 }
595
596 fn as_domain_instantiation(&self) -> Option<DomainId> {
597 match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
598 Some(DomainDigestItem::DomainInstantiated(domain_id)) => Some(domain_id),
599 _ => None,
600 }
601 }
602}
603
604pub(crate) fn evm_chain_id_storage_key() -> StorageKey {
611 StorageKey(
612 storage_prefix(
613 "EVMChainId".as_bytes(),
616 "ChainId".as_bytes(),
618 )
619 .to_vec(),
620 )
621}
622
623pub(crate) fn evm_contract_creation_allowed_by_storage_key() -> StorageKey {
630 StorageKey(
631 storage_prefix(
632 "EVMNoncetracker".as_bytes(),
635 "ContractCreationAllowedBy".as_bytes(),
637 )
638 .to_vec(),
639 )
640}
641
642pub fn domain_total_issuance_storage_key() -> StorageKey {
649 StorageKey(
650 storage_prefix(
651 "Balances".as_bytes(),
653 "TotalIssuance".as_bytes(),
655 )
656 .to_vec(),
657 )
658}
659
660pub fn domain_account_storage_key<AccountId: Encode>(who: AccountId) -> StorageKey {
667 let storage_prefix = storage_prefix("System".as_bytes(), "Account".as_bytes());
668 let key_hashed = who.using_encoded(Blake2_128Concat::hash);
669
670 let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
671
672 final_key.extend_from_slice(&storage_prefix);
673 final_key.extend_from_slice(key_hashed.as_ref());
674
675 StorageKey(final_key)
676}
677
678pub fn self_domain_id_storage_key() -> StorageKey {
683 StorageKey(
684 frame_support::storage::storage_prefix(
685 "SelfDomainId".as_bytes(),
688 "SelfDomainId".as_bytes(),
690 )
691 .to_vec(),
692 )
693}
694
695#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
697pub struct DomainInstanceData {
698 pub runtime_type: RuntimeType,
699 pub raw_genesis: RawGenesis,
700}
701
702#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
703pub struct DomainBundleLimit {
704 pub max_bundle_size: u32,
706 pub max_bundle_weight: Weight,
708}
709
710pub fn calculate_max_bundle_weight_and_size(
714 max_domain_block_size: u32,
715 max_domain_block_weight: Weight,
716 consensus_slot_probability: (u64, u64),
717 bundle_slot_probability: (u64, u64),
718) -> Option<DomainBundleLimit> {
719 let expected_bundles_per_block = bundle_slot_probability
722 .0
723 .checked_mul(consensus_slot_probability.1)?
724 .checked_div(
725 bundle_slot_probability
726 .1
727 .checked_mul(consensus_slot_probability.0)?,
728 )?;
729
730 let max_proof_size = max_domain_block_weight.proof_size();
733 let max_bundle_weight = max_domain_block_weight
734 .checked_div(expected_bundles_per_block)?
735 .set_proof_size(max_proof_size);
736
737 let max_bundle_size =
738 (max_domain_block_size as u64).checked_div(expected_bundles_per_block)? as u32;
739
740 Some(DomainBundleLimit {
741 max_bundle_size,
742 max_bundle_weight,
743 })
744}
745
746pub fn signer_in_tx_range(bundle_vrf_hash: &U256, signer_id_hash: &U256, tx_range: &U256) -> bool {
748 let distance_from_vrf_hash = bidirectional_distance(bundle_vrf_hash, signer_id_hash);
749 distance_from_vrf_hash <= (*tx_range / 2)
750}
751
752#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
754pub enum InvalidReceipt {
755 InvalidBundles,
757}
758
759#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
760pub enum ReceiptValidity {
761 Valid,
762 Invalid(InvalidReceipt),
763}
764
765pub const EMPTY_EXTRINSIC_ROOT: ExtrinsicsRoot = ExtrinsicsRoot {
767 0: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"),
768};
769
770pub fn derive_domain_block_hash<DomainHeader: HeaderT>(
771 domain_block_number: DomainHeader::Number,
772 extrinsics_root: DomainHeader::Hash,
773 state_root: DomainHeader::Hash,
774 parent_domain_block_hash: DomainHeader::Hash,
775 digest: Digest,
776) -> DomainHeader::Hash {
777 let domain_header = DomainHeader::new(
778 domain_block_number,
779 extrinsics_root,
780 state_root,
781 parent_domain_block_hash,
782 digest,
783 );
784
785 domain_header.hash()
786}
787
788#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
790pub enum ExtrinsicDigest {
791 Data(Vec<u8>),
793 Hash(H256),
795}
796
797impl ExtrinsicDigest {
798 pub fn new<Layout: TrieLayout>(ext: Vec<u8>) -> Self
799 where
800 Layout::Hash: HashT,
801 <Layout::Hash as HashT>::Output: Into<H256>,
802 {
803 if let Some(threshold) = Layout::MAX_INLINE_VALUE {
804 if ext.len() >= threshold as usize {
805 ExtrinsicDigest::Hash(Layout::Hash::hash(&ext).into())
806 } else {
807 ExtrinsicDigest::Data(ext)
808 }
809 } else {
810 ExtrinsicDigest::Data(ext)
811 }
812 }
813}
814
815pub trait DomainsTransfersTracker<Balance> {
817 type Error;
818
819 fn initialize_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
821
822 fn note_transfer(
825 from_chain_id: ChainId,
826 to_chain_id: ChainId,
827 amount: Balance,
828 ) -> Result<(), Self::Error>;
829
830 fn confirm_transfer(
832 from_chain_id: ChainId,
833 to_chain_id: ChainId,
834 amount: Balance,
835 ) -> Result<(), Self::Error>;
836
837 fn claim_rejected_transfer(
839 from_chain_id: ChainId,
840 to_chain_id: ChainId,
841 amount: Balance,
842 ) -> Result<(), Self::Error>;
843
844 fn reject_transfer(
846 from_chain_id: ChainId,
847 to_chain_id: ChainId,
848 amount: Balance,
849 ) -> Result<(), Self::Error>;
850
851 fn reduce_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
853}
854
855pub trait DomainOwner<AccountId> {
857 fn is_domain_owner(domain_id: DomainId, acc: AccountId) -> bool;
859}
860
861impl<AccountId> DomainOwner<AccountId> for () {
862 fn is_domain_owner(_domain_id: DomainId, _acc: AccountId) -> bool {
863 false
864 }
865}
866
867pub trait DomainBundleSubmitted {
869 fn domain_bundle_submitted(domain_id: DomainId);
872}
873
874impl DomainBundleSubmitted for () {
875 fn domain_bundle_submitted(_domain_id: DomainId) {}
876}
877
878pub trait OnDomainInstantiated {
880 fn on_domain_instantiated(domain_id: DomainId);
881}
882
883impl OnDomainInstantiated for () {
884 fn on_domain_instantiated(_domain_id: DomainId) {}
885}
886
887#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
889pub struct DomainAllowlistUpdates {
890 pub allow_chains: BTreeSet<ChainId>,
892 pub remove_chains: BTreeSet<ChainId>,
894}
895
896impl DomainAllowlistUpdates {
897 pub fn is_empty(&self) -> bool {
898 self.allow_chains.is_empty() && self.remove_chains.is_empty()
899 }
900
901 pub fn clear(&mut self) {
902 self.allow_chains.clear();
903 self.remove_chains.clear();
904 }
905}
906
907#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
913pub struct DomainSudoCall {
914 pub maybe_call: Option<Vec<u8>>,
915}
916
917impl DomainSudoCall {
918 pub fn clear(&mut self) {
919 self.maybe_call.take();
920 }
921}
922
923#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
929pub struct EvmDomainContractCreationAllowedByCall {
930 pub maybe_call: Option<PermissionedActionAllowedBy<EthereumAccountId>>,
931}
932
933impl EvmDomainContractCreationAllowedByCall {
934 pub fn clear(&mut self) {
935 self.maybe_call.take();
936 }
937}
938
939#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
940pub struct RuntimeObject<Number, Hash> {
941 pub runtime_name: String,
942 pub runtime_type: RuntimeType,
943 pub runtime_upgrades: u32,
944 pub instance_count: u32,
945 pub hash: Hash,
946 pub raw_genesis: RawGenesis,
949 pub version: RuntimeVersion,
950 pub created_at: Number,
951 pub updated_at: Number,
952}
953
954pub fn system_digest_final_key() -> Vec<u8> {
957 frame_support::storage::storage_prefix("System".as_ref(), "Digest".as_ref()).to_vec()
958}
959
960pub trait OnChainRewards<Balance> {
962 fn on_chain_rewards(chain_id: ChainId, reward: Balance);
963}
964
965impl<Balance> OnChainRewards<Balance> for () {
966 fn on_chain_rewards(_chain_id: ChainId, _reward: Balance) {}
967}
968
969#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
970pub enum OperatorRewardSource<Number> {
971 Bundle {
972 at_block_number: Number,
973 },
974 XDMProtocolFees,
975 #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
976 Dummy,
977}
978
979#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
981pub struct BundleAndExecutionReceiptVersion {
982 pub bundle_version: BundleVersion,
983 pub execution_receipt_version: ExecutionReceiptVersion,
984}
985
986#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
988pub struct StorageFeeDeposit<Balance> {
989 pub total_deposited: Balance,
991 pub current_value: Balance,
993}
994
995#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
997pub struct PendingDeposit<Balance> {
998 pub amount: Balance,
1000 pub effective_epoch: EpochIndex,
1002}
1003
1004#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
1006pub struct PendingWithdrawal<Balance, DomainBlockNumber> {
1007 pub stake_withdrawal_amount: Balance,
1009 pub storage_fee_refund: Balance,
1011 pub unlock_at_block: DomainBlockNumber,
1013}
1014
1015#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
1017pub struct NominatorPosition<Balance, DomainBlockNumber, Share> {
1018 pub current_staked_value: Balance,
1020 pub total_shares: Share,
1022 pub storage_fee_deposit: StorageFeeDeposit<Balance>,
1024 pub pending_deposit: Option<PendingDeposit<Balance>>,
1026 pub pending_withdrawals: Vec<PendingWithdrawal<Balance, DomainBlockNumber>>,
1028}
1029
1030sp_api::decl_runtime_apis! {
1031 #[api_version(6)]
1034 pub trait DomainsApi<DomainHeader: HeaderT> {
1035 fn submit_bundle_unsigned(opaque_bundle: OpaqueBundle<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1037
1038 fn submit_receipt_unsigned(singleton_receipt: SealedSingletonReceipt<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1040
1041 fn extract_successful_bundles(
1043 domain_id: DomainId,
1044 extrinsics: Vec<Block::Extrinsic>,
1045 ) -> OpaqueBundles<Block, DomainHeader, Balance>;
1046
1047 fn extrinsics_shuffling_seed() -> Randomness;
1049
1050 fn domain_runtime_code(domain_id: DomainId) -> Option<Vec<u8>>;
1052
1053 fn runtime_id(domain_id: DomainId) -> Option<RuntimeId>;
1055
1056 fn runtime_upgrades() -> Vec<RuntimeId>;
1058
1059 fn domain_instance_data(domain_id: DomainId) -> Option<(DomainInstanceData, NumberFor<Block>)>;
1061
1062 fn domain_timestamp() -> Moment;
1064
1065 fn consensus_transaction_byte_fee() -> Balance;
1068
1069 fn domain_tx_range(domain_id: DomainId) -> U256;
1071
1072 fn genesis_state_root(domain_id: DomainId) -> Option<H256>;
1074
1075 fn head_receipt_number(domain_id: DomainId) -> HeaderNumberFor<DomainHeader>;
1077
1078 fn oldest_unconfirmed_receipt_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1080
1081 fn domain_bundle_limit(domain_id: DomainId) -> Option<DomainBundleLimit>;
1083
1084 fn non_empty_er_exists(domain_id: DomainId) -> bool;
1086
1087 fn domain_best_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1089
1090 fn execution_receipt(receipt_hash: HeaderHashFor<DomainHeader>) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1092
1093 fn domain_operators(domain_id: DomainId) -> Option<(BTreeMap<OperatorId, Balance>, Vec<OperatorId>)>;
1095
1096 fn receipt_hash(domain_id: DomainId, domain_number: HeaderNumberFor<DomainHeader>) -> Option<HeaderHashFor<DomainHeader>>;
1098
1099 fn latest_confirmed_domain_block(domain_id: DomainId) -> Option<(HeaderNumberFor<DomainHeader>, HeaderHashFor<DomainHeader>)>;
1101
1102 fn is_bad_er_pending_to_prune(domain_id: DomainId, receipt_hash: HeaderHashFor<DomainHeader>) -> bool;
1104
1105 fn storage_fund_account_balance(operator_id: OperatorId) -> Balance;
1107
1108 fn is_domain_runtime_upgraded_since(domain_id: DomainId, at: NumberFor<Block>) -> Option<bool>;
1110
1111 fn domain_sudo_call(domain_id: DomainId) -> Option<Vec<u8>>;
1113
1114 fn evm_domain_contract_creation_allowed_by_call(domain_id: DomainId) -> Option<PermissionedActionAllowedBy<EthereumAccountId>>;
1116
1117 fn last_confirmed_domain_block_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1119
1120 fn current_bundle_and_execution_receipt_version() -> BundleAndExecutionReceiptVersion;
1122
1123 fn genesis_execution_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1125
1126 fn nominator_position(
1135 operator_id: OperatorId,
1136 nominator_account: sp_runtime::AccountId32,
1137 ) -> Option<NominatorPosition<Balance, HeaderNumberFor<DomainHeader>, Balance>>;
1138
1139 fn block_pruning_depth() -> NumberFor<Block>;
1142 }
1143
1144 pub trait BundleProducerElectionApi<Balance: Encode + Decode> {
1145 fn bundle_producer_election_params(domain_id: DomainId) -> Option<BundleProducerElectionParams<Balance>>;
1146
1147 fn operator(operator_id: OperatorId) -> Option<(OperatorPublicKey, Balance)>;
1148 }
1149}