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::{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 DomainRuntimeConfig {
430 Evm(EvmDomainRuntimeConfig),
431 AutoId(AutoIdDomainRuntimeConfig),
432}
433
434impl Default for DomainRuntimeConfig {
435 fn default() -> Self {
436 Self::default_evm()
437 }
438}
439
440impl From<EvmDomainRuntimeConfig> for DomainRuntimeConfig {
441 fn from(evm_config: EvmDomainRuntimeConfig) -> Self {
442 DomainRuntimeConfig::Evm(evm_config)
443 }
444}
445
446impl From<AutoIdDomainRuntimeConfig> for DomainRuntimeConfig {
447 fn from(auto_id_config: AutoIdDomainRuntimeConfig) -> Self {
448 DomainRuntimeConfig::AutoId(auto_id_config)
449 }
450}
451
452impl DomainRuntimeConfig {
453 pub fn default_evm() -> Self {
454 DomainRuntimeConfig::Evm(EvmDomainRuntimeConfig::default())
455 }
456
457 pub fn default_auto_id() -> Self {
458 DomainRuntimeConfig::AutoId(AutoIdDomainRuntimeConfig::default())
459 }
460
461 pub fn is_evm_domain(&self) -> bool {
462 matches!(self, DomainRuntimeConfig::Evm(_))
463 }
464
465 pub fn is_auto_id(&self) -> bool {
466 matches!(self, DomainRuntimeConfig::AutoId(_))
467 }
468
469 pub fn evm(&self) -> Option<&EvmDomainRuntimeConfig> {
470 match self {
471 DomainRuntimeConfig::Evm(evm_config) => Some(evm_config),
472 _ => None,
473 }
474 }
475
476 pub fn initial_contract_creation_allow_list(
477 &self,
478 ) -> Option<&PermissionedActionAllowedBy<EthereumAccountId>> {
479 self.evm()
480 .and_then(|evm_config| evm_config.evm_type.initial_contract_creation_allow_list())
481 }
482
483 pub fn auto_id(&self) -> Option<&AutoIdDomainRuntimeConfig> {
484 match self {
485 DomainRuntimeConfig::AutoId(auto_id_config) => Some(auto_id_config),
486 _ => None,
487 }
488 }
489}
490
491#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)]
492pub struct GenesisDomain<AccountId: Ord, Balance> {
493 pub runtime_name: String,
495 pub runtime_type: RuntimeType,
496 pub runtime_version: RuntimeVersion,
497 pub raw_genesis_storage: Vec<u8>,
498
499 pub owner_account_id: AccountId,
501 pub domain_name: String,
502 pub bundle_slot_probability: (u64, u64),
503 pub operator_allow_list: OperatorAllowList<AccountId>,
504 pub domain_runtime_config: DomainRuntimeConfig,
506
507 pub signing_key: OperatorPublicKey,
509 pub minimum_nominator_stake: Balance,
510 pub nomination_tax: Percent,
511
512 pub initial_balances: Vec<(MultiAccountId, Balance)>,
514}
515
516#[derive(
518 Debug, Default, Encode, Decode, TypeInfo, Copy, Clone, PartialEq, Eq, Serialize, Deserialize,
519)]
520pub enum RuntimeType {
521 #[default]
522 Evm,
523 AutoId,
524}
525
526pub type RuntimeId = u32;
528
529pub type EpochIndex = u32;
531
532pub type OperatorId = u64;
534
535pub type ChannelId = sp_core::U256;
537
538#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
540pub enum DomainDigestItem {
541 DomainRuntimeUpgraded(RuntimeId),
542 DomainInstantiated(DomainId),
543}
544
545pub trait DomainsDigestItem {
547 fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self;
548 fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId>;
549
550 fn domain_instantiation(domain_id: DomainId) -> Self;
551 fn as_domain_instantiation(&self) -> Option<DomainId>;
552}
553
554impl DomainsDigestItem for DigestItem {
555 fn domain_runtime_upgrade(runtime_id: RuntimeId) -> Self {
556 Self::Other(DomainDigestItem::DomainRuntimeUpgraded(runtime_id).encode())
557 }
558
559 fn as_domain_runtime_upgrade(&self) -> Option<RuntimeId> {
560 match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
561 Some(DomainDigestItem::DomainRuntimeUpgraded(runtime_id)) => Some(runtime_id),
562 _ => None,
563 }
564 }
565
566 fn domain_instantiation(domain_id: DomainId) -> Self {
567 Self::Other(DomainDigestItem::DomainInstantiated(domain_id).encode())
568 }
569
570 fn as_domain_instantiation(&self) -> Option<DomainId> {
571 match self.try_to::<DomainDigestItem>(OpaqueDigestItemId::Other) {
572 Some(DomainDigestItem::DomainInstantiated(domain_id)) => Some(domain_id),
573 _ => None,
574 }
575 }
576}
577
578pub(crate) fn evm_chain_id_storage_key() -> StorageKey {
585 StorageKey(
586 storage_prefix(
587 "EVMChainId".as_bytes(),
590 "ChainId".as_bytes(),
592 )
593 .to_vec(),
594 )
595}
596
597pub(crate) fn evm_contract_creation_allowed_by_storage_key() -> StorageKey {
604 StorageKey(
605 storage_prefix(
606 "EVMNoncetracker".as_bytes(),
609 "ContractCreationAllowedBy".as_bytes(),
611 )
612 .to_vec(),
613 )
614}
615
616pub fn domain_total_issuance_storage_key() -> StorageKey {
623 StorageKey(
624 storage_prefix(
625 "Balances".as_bytes(),
627 "TotalIssuance".as_bytes(),
629 )
630 .to_vec(),
631 )
632}
633
634pub fn domain_account_storage_key<AccountId: Encode>(who: AccountId) -> StorageKey {
641 let storage_prefix = storage_prefix("System".as_bytes(), "Account".as_bytes());
642 let key_hashed = who.using_encoded(Blake2_128Concat::hash);
643
644 let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
645
646 final_key.extend_from_slice(&storage_prefix);
647 final_key.extend_from_slice(key_hashed.as_ref());
648
649 StorageKey(final_key)
650}
651
652pub fn self_domain_id_storage_key() -> StorageKey {
657 StorageKey(
658 frame_support::storage::storage_prefix(
659 "SelfDomainId".as_bytes(),
662 "SelfDomainId".as_bytes(),
664 )
665 .to_vec(),
666 )
667}
668
669#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
671pub struct DomainInstanceData {
672 pub runtime_type: RuntimeType,
673 pub raw_genesis: RawGenesis,
674}
675
676#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
677pub struct DomainBundleLimit {
678 pub max_bundle_size: u32,
680 pub max_bundle_weight: Weight,
682}
683
684pub fn calculate_max_bundle_weight_and_size(
688 max_domain_block_size: u32,
689 max_domain_block_weight: Weight,
690 consensus_slot_probability: (u64, u64),
691 bundle_slot_probability: (u64, u64),
692) -> Option<DomainBundleLimit> {
693 let expected_bundles_per_block = bundle_slot_probability
696 .0
697 .checked_mul(consensus_slot_probability.1)?
698 .checked_div(
699 bundle_slot_probability
700 .1
701 .checked_mul(consensus_slot_probability.0)?,
702 )?;
703
704 let max_proof_size = max_domain_block_weight.proof_size();
707 let max_bundle_weight = max_domain_block_weight
708 .checked_div(expected_bundles_per_block)?
709 .set_proof_size(max_proof_size);
710
711 let max_bundle_size =
712 (max_domain_block_size as u64).checked_div(expected_bundles_per_block)? as u32;
713
714 Some(DomainBundleLimit {
715 max_bundle_size,
716 max_bundle_weight,
717 })
718}
719
720pub fn signer_in_tx_range(bundle_vrf_hash: &U256, signer_id_hash: &U256, tx_range: &U256) -> bool {
722 let distance_from_vrf_hash = bidirectional_distance(bundle_vrf_hash, signer_id_hash);
723 distance_from_vrf_hash <= (*tx_range / 2)
724}
725
726#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
728pub enum InvalidReceipt {
729 InvalidBundles,
731}
732
733#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
734pub enum ReceiptValidity {
735 Valid,
736 Invalid(InvalidReceipt),
737}
738
739pub const EMPTY_EXTRINSIC_ROOT: ExtrinsicsRoot = ExtrinsicsRoot {
741 0: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"),
742};
743
744pub fn derive_domain_block_hash<DomainHeader: HeaderT>(
745 domain_block_number: DomainHeader::Number,
746 extrinsics_root: DomainHeader::Hash,
747 state_root: DomainHeader::Hash,
748 parent_domain_block_hash: DomainHeader::Hash,
749 digest: Digest,
750) -> DomainHeader::Hash {
751 let domain_header = DomainHeader::new(
752 domain_block_number,
753 extrinsics_root,
754 state_root,
755 parent_domain_block_hash,
756 digest,
757 );
758
759 domain_header.hash()
760}
761
762#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
764pub enum ExtrinsicDigest {
765 Data(Vec<u8>),
767 Hash(H256),
769}
770
771impl ExtrinsicDigest {
772 pub fn new<Layout: TrieLayout>(ext: Vec<u8>) -> Self
773 where
774 Layout::Hash: HashT,
775 <Layout::Hash as HashT>::Output: Into<H256>,
776 {
777 if let Some(threshold) = Layout::MAX_INLINE_VALUE {
778 if ext.len() >= threshold as usize {
779 ExtrinsicDigest::Hash(Layout::Hash::hash(&ext).into())
780 } else {
781 ExtrinsicDigest::Data(ext)
782 }
783 } else {
784 ExtrinsicDigest::Data(ext)
785 }
786 }
787}
788
789pub trait DomainsTransfersTracker<Balance> {
791 type Error;
792
793 fn initialize_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
795
796 fn note_transfer(
799 from_chain_id: ChainId,
800 to_chain_id: ChainId,
801 amount: Balance,
802 ) -> Result<(), Self::Error>;
803
804 fn confirm_transfer(
806 from_chain_id: ChainId,
807 to_chain_id: ChainId,
808 amount: Balance,
809 ) -> Result<(), Self::Error>;
810
811 fn claim_rejected_transfer(
813 from_chain_id: ChainId,
814 to_chain_id: ChainId,
815 amount: Balance,
816 ) -> Result<(), Self::Error>;
817
818 fn reject_transfer(
820 from_chain_id: ChainId,
821 to_chain_id: ChainId,
822 amount: Balance,
823 ) -> Result<(), Self::Error>;
824
825 fn reduce_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>;
827}
828
829pub trait DomainOwner<AccountId> {
831 fn is_domain_owner(domain_id: DomainId, acc: AccountId) -> bool;
833}
834
835impl<AccountId> DomainOwner<AccountId> for () {
836 fn is_domain_owner(_domain_id: DomainId, _acc: AccountId) -> bool {
837 false
838 }
839}
840
841pub trait DomainBundleSubmitted {
843 fn domain_bundle_submitted(domain_id: DomainId);
846}
847
848impl DomainBundleSubmitted for () {
849 fn domain_bundle_submitted(_domain_id: DomainId) {}
850}
851
852pub trait OnDomainInstantiated {
854 fn on_domain_instantiated(domain_id: DomainId);
855}
856
857impl OnDomainInstantiated for () {
858 fn on_domain_instantiated(_domain_id: DomainId) {}
859}
860
861#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
863pub struct DomainAllowlistUpdates {
864 pub allow_chains: BTreeSet<ChainId>,
866 pub remove_chains: BTreeSet<ChainId>,
868}
869
870impl DomainAllowlistUpdates {
871 pub fn is_empty(&self) -> bool {
872 self.allow_chains.is_empty() && self.remove_chains.is_empty()
873 }
874
875 pub fn clear(&mut self) {
876 self.allow_chains.clear();
877 self.remove_chains.clear();
878 }
879}
880
881#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
887pub struct DomainSudoCall {
888 pub maybe_call: Option<Vec<u8>>,
889}
890
891impl DomainSudoCall {
892 pub fn clear(&mut self) {
893 self.maybe_call.take();
894 }
895}
896
897#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
903pub struct EvmDomainContractCreationAllowedByCall {
904 pub maybe_call: Option<PermissionedActionAllowedBy<EthereumAccountId>>,
905}
906
907impl EvmDomainContractCreationAllowedByCall {
908 pub fn clear(&mut self) {
909 self.maybe_call.take();
910 }
911}
912
913#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
914pub struct RuntimeObject<Number, Hash> {
915 pub runtime_name: String,
916 pub runtime_type: RuntimeType,
917 pub runtime_upgrades: u32,
918 pub instance_count: u32,
919 pub hash: Hash,
920 pub raw_genesis: RawGenesis,
923 pub version: RuntimeVersion,
924 pub created_at: Number,
925 pub updated_at: Number,
926}
927
928pub fn system_digest_final_key() -> Vec<u8> {
931 frame_support::storage::storage_prefix("System".as_ref(), "Digest".as_ref()).to_vec()
932}
933
934pub trait OnChainRewards<Balance> {
936 fn on_chain_rewards(chain_id: ChainId, reward: Balance);
937}
938
939impl<Balance> OnChainRewards<Balance> for () {
940 fn on_chain_rewards(_chain_id: ChainId, _reward: Balance) {}
941}
942
943#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
944pub enum OperatorRewardSource<Number> {
945 Bundle {
946 at_block_number: Number,
947 },
948 XDMProtocolFees,
949 #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
950 Dummy,
951}
952
953#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
955pub struct BundleAndExecutionReceiptVersion {
956 pub bundle_version: BundleVersion,
957 pub execution_receipt_version: ExecutionReceiptVersion,
958}
959
960#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
962pub struct StorageFeeDeposit<Balance> {
963 pub total_deposited: Balance,
965 pub current_value: Balance,
967}
968
969#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
971pub struct PendingDeposit<Balance> {
972 pub amount: Balance,
974 pub effective_epoch: EpochIndex,
976}
977
978#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
980pub struct PendingWithdrawal<Balance, DomainBlockNumber> {
981 pub stake_withdrawal_amount: Balance,
983 pub storage_fee_refund: Balance,
985 pub unlock_at_block: DomainBlockNumber,
987}
988
989#[derive(Debug, Encode, Decode, TypeInfo, Clone, PartialEq, Eq)]
991pub struct NominatorPosition<Balance, DomainBlockNumber, Share> {
992 pub current_staked_value: Balance,
994 pub total_shares: Share,
996 pub storage_fee_deposit: StorageFeeDeposit<Balance>,
998 pub pending_deposit: Option<PendingDeposit<Balance>>,
1000 pub pending_withdrawals: Vec<PendingWithdrawal<Balance, DomainBlockNumber>>,
1002}
1003
1004sp_api::decl_runtime_apis! {
1005 #[api_version(6)]
1008 pub trait DomainsApi<DomainHeader: HeaderT> {
1009 fn submit_bundle_unsigned(opaque_bundle: OpaqueBundle<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1011
1012 fn submit_receipt_unsigned(singleton_receipt: SealedSingletonReceipt<NumberFor<Block>, Block::Hash, DomainHeader, Balance>);
1014
1015 fn extract_successful_bundles(
1017 domain_id: DomainId,
1018 extrinsics: Vec<Block::Extrinsic>,
1019 ) -> OpaqueBundles<Block, DomainHeader, Balance>;
1020
1021 fn extrinsics_shuffling_seed() -> Randomness;
1023
1024 fn domain_runtime_code(domain_id: DomainId) -> Option<Vec<u8>>;
1026
1027 fn runtime_id(domain_id: DomainId) -> Option<RuntimeId>;
1029
1030 fn runtime_upgrades() -> Vec<RuntimeId>;
1032
1033 fn domain_instance_data(domain_id: DomainId) -> Option<(DomainInstanceData, NumberFor<Block>)>;
1035
1036 fn domain_timestamp() -> Moment;
1038
1039 fn consensus_transaction_byte_fee() -> Balance;
1042
1043 fn domain_tx_range(domain_id: DomainId) -> U256;
1045
1046 fn genesis_state_root(domain_id: DomainId) -> Option<H256>;
1048
1049 fn head_receipt_number(domain_id: DomainId) -> HeaderNumberFor<DomainHeader>;
1051
1052 fn oldest_unconfirmed_receipt_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1054
1055 fn domain_bundle_limit(domain_id: DomainId) -> Option<DomainBundleLimit>;
1057
1058 fn non_empty_er_exists(domain_id: DomainId) -> bool;
1060
1061 fn domain_best_number(domain_id: DomainId) -> Option<HeaderNumberFor<DomainHeader>>;
1063
1064 fn execution_receipt(receipt_hash: HeaderHashFor<DomainHeader>) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1066
1067 fn domain_operators(domain_id: DomainId) -> Option<(BTreeMap<OperatorId, Balance>, Vec<OperatorId>)>;
1069
1070 fn receipt_hash(domain_id: DomainId, domain_number: HeaderNumberFor<DomainHeader>) -> Option<HeaderHashFor<DomainHeader>>;
1072
1073 fn latest_confirmed_domain_block(domain_id: DomainId) -> Option<(HeaderNumberFor<DomainHeader>, HeaderHashFor<DomainHeader>)>;
1075
1076 fn is_bad_er_pending_to_prune(domain_id: DomainId, receipt_hash: HeaderHashFor<DomainHeader>) -> bool;
1078
1079 fn storage_fund_account_balance(operator_id: OperatorId) -> Balance;
1081
1082 fn is_domain_runtime_upgraded_since(domain_id: DomainId, at: NumberFor<Block>) -> Option<bool>;
1084
1085 fn domain_sudo_call(domain_id: DomainId) -> Option<Vec<u8>>;
1087
1088 fn evm_domain_contract_creation_allowed_by_call(domain_id: DomainId) -> Option<PermissionedActionAllowedBy<EthereumAccountId>>;
1090
1091 fn last_confirmed_domain_block_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1093
1094 fn current_bundle_and_execution_receipt_version() -> BundleAndExecutionReceiptVersion;
1096
1097 fn genesis_execution_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>;
1099
1100 fn nominator_position(
1109 operator_id: OperatorId,
1110 nominator_account: sp_runtime::AccountId32,
1111 ) -> Option<NominatorPosition<Balance, HeaderNumberFor<DomainHeader>, Balance>>;
1112
1113 fn block_pruning_depth() -> NumberFor<Block>;
1116 }
1117
1118 pub trait BundleProducerElectionApi<Balance: Encode + Decode> {
1119 fn bundle_producer_election_params(domain_id: DomainId) -> Option<BundleProducerElectionParams<Balance>>;
1120
1121 fn operator(operator_id: OperatorId) -> Option<(OperatorPublicKey, Balance)>;
1122 }
1123}