1#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6use crate::block_tree::import_genesis_receipt;
7use crate::pallet::DomainStakingSummary;
8use crate::staking::StakingSummary;
9use crate::{
10 BalanceOf, Config, DomainHashingFor, DomainRegistry, DomainSudoCalls, EvmChainIds,
11 ExecutionReceiptOf, HoldIdentifier, NextDomainId, RuntimeRegistry, into_complete_raw_genesis,
12};
13#[cfg(not(feature = "std"))]
14use alloc::string::String;
15#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17use domain_runtime_primitives::MultiAccountId;
18use frame_support::traits::fungible::{Inspect, Mutate, MutateHold};
19use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
20use frame_support::weights::Weight;
21use frame_support::{PalletError, ensure};
22use frame_system::pallet_prelude::*;
23use parity_scale_codec::{Decode, Encode};
24use scale_info::TypeInfo;
25use sp_core::Get;
26use sp_domains::{
27 DomainBundleLimit, DomainId, DomainRuntimeInfo, DomainSudoCall, DomainsDigestItem,
28 DomainsTransfersTracker, OnDomainInstantiated, OperatorAllowList, RuntimeId, RuntimeType,
29 calculate_max_bundle_weight_and_size, derive_domain_block_hash,
30};
31use sp_runtime::DigestItem;
32use sp_runtime::traits::{CheckedAdd, Zero};
33use sp_std::collections::btree_map::BTreeMap;
34use sp_std::collections::btree_set::BTreeSet;
35
36#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)]
38pub enum Error {
39 ExceedMaxDomainBlockWeight,
40 ExceedMaxDomainBlockSize,
41 MaxDomainId,
42 InvalidEVMChainId,
43 InvalidSlotProbability,
44 RuntimeNotFound,
45 InsufficientFund,
46 DomainNameTooLong,
47 BalanceFreeze,
48 FailedToGenerateGenesisStateRoot,
49 DomainNotFound,
50 NotDomainOwner,
51 InitialBalanceOverflow,
52 TransfersTracker,
53 MinInitialAccountBalance,
54 MaxInitialDomainAccounts,
55 DuplicateInitialAccounts,
56 FailedToGenerateRawGenesis(crate::runtime_registry::Error),
57 BundleLimitCalculationOverflow,
58 InvalidConfigForRuntimeType,
59}
60
61#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
62pub struct DomainConfig<AccountId: Ord, Balance> {
63 pub domain_name: String,
65 pub runtime_id: RuntimeId,
67 pub max_bundle_size: u32,
69 pub max_bundle_weight: Weight,
71 pub bundle_slot_probability: (u64, u64),
74 pub operator_allow_list: OperatorAllowList<AccountId>,
76 pub initial_balances: Vec<(MultiAccountId, Balance)>,
78}
79
80#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
86pub struct DomainConfigParams<AccountId: Ord, Balance> {
87 pub domain_name: String,
88 pub runtime_id: RuntimeId,
89 pub maybe_bundle_limit: Option<DomainBundleLimit>,
90 pub bundle_slot_probability: (u64, u64),
91 pub operator_allow_list: OperatorAllowList<AccountId>,
92 pub initial_balances: Vec<(MultiAccountId, Balance)>,
93 pub domain_runtime_info: DomainRuntimeInfo,
97}
98
99pub fn into_domain_config<T: Config>(
100 domain_config_params: DomainConfigParams<T::AccountId, BalanceOf<T>>,
101) -> Result<DomainConfig<T::AccountId, BalanceOf<T>>, Error> {
102 let DomainConfigParams {
103 domain_name,
104 runtime_id,
105 maybe_bundle_limit,
106 bundle_slot_probability,
107 operator_allow_list,
108 initial_balances,
109 domain_runtime_info: _,
110 } = domain_config_params;
111
112 let DomainBundleLimit {
113 max_bundle_size,
114 max_bundle_weight,
115 } = match maybe_bundle_limit {
116 Some(b) => b,
117 None => calculate_max_bundle_weight_and_size(
118 T::MaxDomainBlockSize::get(),
119 T::MaxDomainBlockWeight::get(),
120 T::ConsensusSlotProbability::get(),
121 bundle_slot_probability,
122 )
123 .ok_or(Error::BundleLimitCalculationOverflow)?,
124 };
125
126 Ok(DomainConfig {
127 domain_name,
128 runtime_id,
129 max_bundle_size,
130 max_bundle_weight,
131 bundle_slot_probability,
132 operator_allow_list,
133 initial_balances,
134 })
135}
136
137impl<AccountId, Balance> DomainConfig<AccountId, Balance>
138where
139 AccountId: Ord,
140 Balance: Zero + CheckedAdd + PartialOrd,
141{
142 pub(crate) fn total_issuance(&self) -> Option<Balance> {
143 self.initial_balances
144 .iter()
145 .try_fold(Balance::zero(), |total, (_, balance)| {
146 total.checked_add(balance)
147 })
148 }
149
150 pub(crate) fn check_initial_balances<T: Config>(&self) -> Result<(), Error>
151 where
152 Balance: From<BalanceOf<T>>,
153 {
154 let accounts: BTreeSet<MultiAccountId> = self
155 .initial_balances
156 .iter()
157 .map(|(acc, _)| acc)
158 .cloned()
159 .collect();
160
161 ensure!(
162 accounts.len() == self.initial_balances.len(),
163 Error::DuplicateInitialAccounts
164 );
165
166 ensure!(
167 self.initial_balances.len() as u32 <= T::MaxInitialDomainAccounts::get(),
168 Error::MaxInitialDomainAccounts
169 );
170
171 for (_, balance) in &self.initial_balances {
172 ensure!(
173 *balance >= T::MinInitialDomainAccountBalance::get().into(),
174 Error::MinInitialAccountBalance
175 );
176 }
177
178 Ok(())
179 }
180}
181
182#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
183pub struct DomainObject<Number, ReceiptHash, AccountId: Ord, Balance> {
184 pub owner_account_id: AccountId,
186 pub created_at: Number,
188 pub genesis_receipt_hash: ReceiptHash,
190 pub domain_config: DomainConfig<AccountId, Balance>,
192 pub domain_runtime_info: DomainRuntimeInfo,
194 pub domain_instantiation_deposit: Balance,
196}
197
198pub(crate) fn can_instantiate_domain<T: Config>(
199 owner_account_id: &T::AccountId,
200 domain_config_params: DomainConfigParams<T::AccountId, BalanceOf<T>>,
201) -> Result<DomainConfig<T::AccountId, BalanceOf<T>>, Error> {
202 let (numerator, denominator) = domain_config_params.bundle_slot_probability;
204 ensure!(
205 numerator != 0 && denominator != 0 && numerator <= denominator,
206 Error::InvalidSlotProbability
207 );
208
209 let domain_config = into_domain_config::<T>(domain_config_params)?;
210
211 ensure!(
212 domain_config.domain_name.len() as u32 <= T::MaxDomainNameLength::get(),
213 Error::DomainNameTooLong,
214 );
215 ensure!(
216 RuntimeRegistry::<T>::contains_key(domain_config.runtime_id),
217 Error::RuntimeNotFound
218 );
219 ensure!(
220 domain_config.max_bundle_size <= T::MaxDomainBlockSize::get(),
221 Error::ExceedMaxDomainBlockSize
222 );
223 ensure!(
224 domain_config
225 .max_bundle_weight
226 .all_lte(T::MaxDomainBlockWeight::get()),
227 Error::ExceedMaxDomainBlockWeight
228 );
229
230 ensure!(
231 T::Currency::reducible_balance(owner_account_id, Preservation::Protect, Fortitude::Polite)
232 >= T::DomainInstantiationDeposit::get(),
233 Error::InsufficientFund
234 );
235
236 domain_config.check_initial_balances::<T>()?;
237
238 Ok(domain_config)
239}
240
241pub(crate) fn do_instantiate_domain<T: Config>(
242 domain_config_params: DomainConfigParams<T::AccountId, BalanceOf<T>>,
243 owner_account_id: T::AccountId,
244 created_at: BlockNumberFor<T>,
245) -> Result<DomainId, Error> {
246 let domain_runtime_info = domain_config_params.domain_runtime_info.clone();
247 let domain_config = can_instantiate_domain::<T>(&owner_account_id, domain_config_params)?;
248
249 let domain_instantiation_deposit = T::DomainInstantiationDeposit::get();
250 let domain_id = NextDomainId::<T>::get();
251 let runtime_obj = RuntimeRegistry::<T>::mutate(domain_config.runtime_id, |maybe_runtime_obj| {
252 let mut runtime_object = maybe_runtime_obj
253 .take()
254 .expect("Runtime object must exist as checked in `can_instantiate_domain`; qed");
255 runtime_object.instance_count = runtime_object.instance_count.saturating_add(1);
256 *maybe_runtime_obj = Some(runtime_object.clone());
257 runtime_object
258 });
259
260 let domain_runtime_info = match (runtime_obj.runtime_type, domain_runtime_info) {
261 (
262 RuntimeType::Evm,
263 DomainRuntimeInfo::Evm {
264 chain_id,
265 domain_runtime_config,
266 },
267 ) => {
268 ensure!(
270 !EvmChainIds::<T>::contains_key(chain_id),
271 Error::InvalidEVMChainId
272 );
273
274 EvmChainIds::<T>::insert(chain_id, domain_id);
276
277 DomainRuntimeInfo::Evm {
278 chain_id,
279 domain_runtime_config,
280 }
281 }
282 (
283 RuntimeType::AutoId,
284 DomainRuntimeInfo::AutoId {
285 domain_runtime_config,
286 },
287 ) => DomainRuntimeInfo::AutoId {
288 domain_runtime_config,
289 },
290 _ => return Err(Error::InvalidConfigForRuntimeType),
291 };
292
293 let total_issuance = domain_config
295 .total_issuance()
296 .ok_or(Error::InitialBalanceOverflow)?;
297
298 T::Currency::burn_from(
299 &owner_account_id,
300 total_issuance,
301 Preservation::Expendable,
302 Precision::Exact,
303 Fortitude::Polite,
304 )
305 .map_err(|_| Error::InsufficientFund)?;
306
307 T::DomainsTransfersTracker::initialize_domain_balance(domain_id, total_issuance)
308 .map_err(|_| Error::TransfersTracker)?;
309
310 let genesis_receipt = {
311 let state_version = runtime_obj.version.state_version();
312 let raw_genesis = into_complete_raw_genesis::<T>(
313 runtime_obj,
314 domain_id,
315 &domain_runtime_info,
316 total_issuance,
317 domain_config.initial_balances.clone(),
318 )
319 .map_err(Error::FailedToGenerateRawGenesis)?;
320 let state_root = raw_genesis.state_root::<DomainHashingFor<T>>(state_version);
321 let genesis_block_hash = derive_domain_block_hash::<T::DomainHeader>(
322 Zero::zero(),
323 sp_domains::EMPTY_EXTRINSIC_ROOT.into(),
324 state_root,
325 Default::default(),
326 Default::default(),
327 );
328
329 ExecutionReceiptOf::<T>::genesis(
330 state_root,
331 sp_domains::EMPTY_EXTRINSIC_ROOT.into(),
332 genesis_block_hash,
333 T::CurrentBundleAndExecutionReceiptVersion::get().execution_receipt_version,
334 )
335 };
336 let genesis_receipt_hash = genesis_receipt.hash::<DomainHashingFor<T>>();
337
338 let domain_obj = DomainObject {
339 owner_account_id: owner_account_id.clone(),
340 created_at,
341 genesis_receipt_hash,
342 domain_config,
343 domain_runtime_info,
344 domain_instantiation_deposit,
345 };
346 DomainRegistry::<T>::insert(domain_id, domain_obj);
347
348 let next_domain_id = domain_id.checked_add(&1.into()).ok_or(Error::MaxDomainId)?;
349 NextDomainId::<T>::set(next_domain_id);
350
351 T::Currency::hold(
353 &T::HoldIdentifier::domain_instantiation_id(),
354 &owner_account_id,
355 domain_instantiation_deposit,
356 )
357 .map_err(|_| Error::BalanceFreeze)?;
358
359 DomainStakingSummary::<T>::insert(
360 domain_id,
361 StakingSummary {
362 current_epoch_index: 0,
363 current_total_stake: Zero::zero(),
364 current_operators: BTreeMap::new(),
365 next_operators: BTreeSet::new(),
366 current_epoch_rewards: BTreeMap::new(),
367 },
368 );
369
370 import_genesis_receipt::<T>(domain_id, genesis_receipt);
371 T::OnDomainInstantiated::on_domain_instantiated(domain_id);
372
373 DomainSudoCalls::<T>::insert(domain_id, DomainSudoCall { maybe_call: None });
374
375 frame_system::Pallet::<T>::deposit_log(DigestItem::domain_instantiation(domain_id));
376
377 Ok(domain_id)
378}
379
380pub(crate) fn do_update_domain_allow_list<T: Config>(
381 domain_owner: T::AccountId,
382 domain_id: DomainId,
383 updated_operator_allow_list: OperatorAllowList<T::AccountId>,
384) -> Result<(), Error> {
385 let mut domain_obj = DomainRegistry::<T>::get(domain_id).ok_or(Error::DomainNotFound)?;
386
387 ensure!(
388 domain_obj.owner_account_id == domain_owner,
389 Error::NotDomainOwner,
390 );
391
392 domain_obj.domain_config.operator_allow_list = updated_operator_allow_list;
393 DomainRegistry::<T>::insert(domain_id, domain_obj);
394
395 Ok(())
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::tests::{TEST_RUNTIME_APIS, Test, new_test_ext};
402 use domain_runtime_primitives::{AccountId20, AccountId20Converter, DEFAULT_EVM_CHAIN_ID};
403 use frame_support::traits::Currency;
404 use frame_support::{assert_err, assert_ok};
405 use hex_literal::hex;
406 use sp_domains::storage::RawGenesis;
407 use sp_domains::{EvmDomainRuntimeConfig, EvmType, PermissionedActionAllowedBy, RuntimeObject};
408 use sp_runtime::traits::Convert;
409 use sp_std::vec;
410 use sp_version::{RuntimeVersion, create_apis_vec};
411 use subspace_runtime_primitives::AI3;
412
413 type Balances = pallet_balances::Pallet<Test>;
414
415 #[test]
416 fn test_domain_instantiation() {
417 let creator = 1u128;
418 let created_at = 0u32;
419 let mut domain_config_params = DomainConfigParams {
421 domain_name: String::from_utf8(vec![0; 1024]).unwrap(),
422 runtime_id: 0,
423 maybe_bundle_limit: Some(DomainBundleLimit {
424 max_bundle_size: u32::MAX,
425 max_bundle_weight: Weight::MAX,
426 }),
427 bundle_slot_probability: (0, 0),
428 operator_allow_list: OperatorAllowList::Anyone,
429 initial_balances: Default::default(),
430 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
431 };
432
433 let mut ext = new_test_ext();
434 ext.execute_with(|| {
435 assert_eq!(NextDomainId::<Test>::get(), 0.into());
436
437 assert_eq!(
439 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
440 Err(Error::InvalidSlotProbability)
441 );
442 domain_config_params.bundle_slot_probability = (1, 0);
443 assert_eq!(
444 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
445 Err(Error::InvalidSlotProbability)
446 );
447 domain_config_params.bundle_slot_probability = (0, 1);
448 assert_eq!(
449 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
450 Err(Error::InvalidSlotProbability)
451 );
452 domain_config_params.bundle_slot_probability = (2, 1);
453 assert_eq!(
454 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
455 Err(Error::InvalidSlotProbability)
456 );
457 domain_config_params.bundle_slot_probability = (1, 1);
459
460 assert_eq!(
462 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
463 Err(Error::DomainNameTooLong)
464 );
465 "evm-domain".clone_into(&mut domain_config_params.domain_name);
467
468 assert_eq!(
470 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
471 Err(Error::RuntimeNotFound)
472 );
473 RuntimeRegistry::<Test>::insert(
475 domain_config_params.runtime_id,
476 RuntimeObject {
477 runtime_name: "evm".to_owned(),
478 runtime_type: Default::default(),
479 runtime_upgrades: 0,
480 hash: Default::default(),
481 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
482 version: RuntimeVersion {
483 spec_name: "test".into(),
484 spec_version: 1,
485 impl_version: 1,
486 transaction_version: 1,
487 apis: create_apis_vec!(TEST_RUNTIME_APIS),
488 ..Default::default()
489 },
490 created_at: Default::default(),
491 updated_at: Default::default(),
492 instance_count: 0,
493 },
494 );
495
496 assert_eq!(
498 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
499 Err(Error::ExceedMaxDomainBlockSize)
500 );
501 domain_config_params
503 .maybe_bundle_limit
504 .as_mut()
505 .unwrap()
506 .max_bundle_size = 1;
507
508 assert_eq!(
510 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
511 Err(Error::ExceedMaxDomainBlockWeight)
512 );
513 domain_config_params
515 .maybe_bundle_limit
516 .as_mut()
517 .unwrap()
518 .max_bundle_weight = Weight::from_parts(1, 0);
519
520 assert_eq!(
522 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
523 Err(Error::InsufficientFund)
524 );
525 Balances::make_free_balance_be(
527 &creator,
528 <Test as Config>::DomainInstantiationDeposit::get()
529 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
530 );
531 domain_config_params.maybe_bundle_limit = None;
533
534 let domain_id =
536 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
537 .unwrap();
538 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
539
540 assert_eq!(domain_obj.owner_account_id, creator);
541 assert_eq!(domain_obj.created_at, created_at);
542 assert_eq!(
543 domain_obj.domain_config,
544 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
545 );
546 assert_eq!(NextDomainId::<Test>::get(), 1.into());
547 assert_eq!(Balances::usable_balance(creator), Zero::zero());
549
550 let runtime_obj =
552 RuntimeRegistry::<Test>::get(domain_config_params.runtime_id).unwrap();
553 assert_eq!(runtime_obj.instance_count, 1);
554
555 assert_eq!(
557 do_instantiate_domain::<Test>(domain_config_params, creator, created_at),
558 Err(Error::InsufficientFund)
559 );
560
561 let updated_operator_allow_list =
563 OperatorAllowList::Operators(BTreeSet::from_iter(vec![1, 2, 3]));
564 assert_ok!(do_update_domain_allow_list::<Test>(
565 creator,
566 domain_id,
567 updated_operator_allow_list.clone()
568 ));
569 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
570 assert_eq!(
571 domain_obj.domain_config.operator_allow_list,
572 updated_operator_allow_list
573 );
574 });
575 }
576
577 #[test]
578 fn test_domain_instantiation_evm_accounts() {
579 let creator = 1u128;
580 let created_at = 0u32;
581 let mut domain_config_params = DomainConfigParams {
583 domain_name: "evm-domain".to_owned(),
584 runtime_id: 0,
585 maybe_bundle_limit: None,
586 bundle_slot_probability: (1, 1),
587 operator_allow_list: OperatorAllowList::Anyone,
588 initial_balances: vec![(MultiAccountId::Raw(vec![0, 1, 2, 3, 4, 5]), 1_000_000 * AI3)],
589 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
590 };
591
592 let mut ext = new_test_ext();
593 ext.execute_with(|| {
594 assert_eq!(NextDomainId::<Test>::get(), 0.into());
595 RuntimeRegistry::<Test>::insert(
597 domain_config_params.runtime_id,
598 RuntimeObject {
599 runtime_name: "evm".to_owned(),
600 runtime_type: Default::default(),
601 runtime_upgrades: 0,
602 hash: Default::default(),
603 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
604 version: RuntimeVersion {
605 spec_name: "test".into(),
606 spec_version: 1,
607 impl_version: 1,
608 transaction_version: 1,
609 apis: create_apis_vec!(TEST_RUNTIME_APIS),
610 ..Default::default()
611 },
612 created_at: Default::default(),
613 updated_at: Default::default(),
614 instance_count: 0,
615 },
616 );
617
618 Balances::make_free_balance_be(
620 &creator,
621 <Test as Config>::DomainInstantiationDeposit::get()
622 + 1_000_000 * AI3
624 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
625 );
626
627 assert_err!(
629 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
630 Error::FailedToGenerateRawGenesis(
631 crate::runtime_registry::Error::InvalidAccountIdType
632 )
633 );
634
635 domain_config_params.initial_balances = vec![
637 (
638 AccountId20Converter::convert(AccountId20::from(hex!(
639 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
640 ))),
641 1_000_000 * AI3,
642 ),
643 (
644 AccountId20Converter::convert(AccountId20::from(hex!(
645 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
646 ))),
647 1_000_000 * AI3,
648 ),
649 ];
650
651 assert_err!(
652 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
653 Error::DuplicateInitialAccounts
654 );
655
656 domain_config_params.initial_balances = vec![
658 (
659 AccountId20Converter::convert(AccountId20::from(hex!(
660 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
661 ))),
662 1_000_000 * AI3,
663 ),
664 (
665 AccountId20Converter::convert(AccountId20::from(hex!(
666 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cbc"
667 ))),
668 1_000_000 * AI3,
669 ),
670 (
671 AccountId20Converter::convert(AccountId20::from(hex!(
672 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566ccc"
673 ))),
674 1_000_000 * AI3,
675 ),
676 (
677 AccountId20Converter::convert(AccountId20::from(hex!(
678 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cdc"
679 ))),
680 1_000_000 * AI3,
681 ),
682 (
683 AccountId20Converter::convert(AccountId20::from(hex!(
684 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cec"
685 ))),
686 1_000_000 * AI3,
687 ),
688 (
689 AccountId20Converter::convert(AccountId20::from(hex!(
690 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cfc"
691 ))),
692 1_000_000 * AI3,
693 ),
694 ];
695
696 assert_err!(
697 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
698 Error::MaxInitialDomainAccounts
699 );
700
701 domain_config_params.initial_balances = vec![(
703 AccountId20Converter::convert(AccountId20::from(hex!(
704 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
705 ))),
706 1,
707 )];
708
709 assert_err!(
710 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
711 Error::MinInitialAccountBalance
712 );
713
714 domain_config_params.initial_balances = vec![(
715 AccountId20Converter::convert(AccountId20::from(hex!(
716 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
717 ))),
718 1_000_000 * AI3,
719 )];
720
721 Balances::make_free_balance_be(
723 &creator,
724 <Test as Config>::DomainInstantiationDeposit::get()
725 + 1_000_000 * AI3
727 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
728 );
729
730 domain_config_params.domain_runtime_info =
732 (DEFAULT_EVM_CHAIN_ID + 1, Default::default()).into();
733 let domain_id =
734 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
735 .unwrap();
736 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
737
738 assert_eq!(domain_obj.owner_account_id, creator);
739 assert_eq!(domain_obj.created_at, created_at);
740 assert_eq!(
741 domain_obj.domain_config,
742 into_domain_config::<Test>(domain_config_params).unwrap()
743 );
744 });
745 }
746
747 #[test]
748 fn test_domain_instantiation_evm_contract_allow_list() {
749 let creator = 1u128;
750 let created_at = 0u32;
751 let mut domain_config_params = DomainConfigParams {
753 domain_name: "evm-domain".to_owned(),
754 runtime_id: 0,
755 maybe_bundle_limit: None,
756 bundle_slot_probability: (1, 1),
757 operator_allow_list: OperatorAllowList::Anyone,
758 initial_balances: vec![(
759 AccountId20Converter::convert(AccountId20::from(hex!(
760 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
761 ))),
762 1_000_000 * AI3,
763 )],
764 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
765 };
766
767 let mut ext = new_test_ext();
768 ext.execute_with(|| {
769 assert_eq!(NextDomainId::<Test>::get(), 0.into());
770 RuntimeRegistry::<Test>::insert(
772 domain_config_params.runtime_id,
773 RuntimeObject {
774 runtime_name: "evm".to_owned(),
775 runtime_type: Default::default(),
776 runtime_upgrades: 0,
777 hash: Default::default(),
778 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
779 version: RuntimeVersion {
780 spec_name: "test".into(),
781 spec_version: 1,
782 impl_version: 1,
783 transaction_version: 1,
784 apis: create_apis_vec!(TEST_RUNTIME_APIS),
785 ..Default::default()
786 },
787 created_at: Default::default(),
788 updated_at: Default::default(),
789 instance_count: 0,
790 },
791 );
792
793 Balances::make_free_balance_be(
795 &creator,
796 <Test as Config>::DomainInstantiationDeposit::get()
797 + 1_000_000 * AI3
799 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
800 );
801
802 let domain_id =
804 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
805 .unwrap();
806 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
807
808 assert_eq!(domain_obj.owner_account_id, creator);
809 assert_eq!(domain_obj.created_at, created_at);
810 assert_eq!(
811 domain_obj.domain_config,
812 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
813 );
814 assert_eq!(
815 domain_obj
816 .domain_runtime_info
817 .initial_contract_creation_allow_list(),
818 None,
819 "default is public EVM, which does not have a contract creation allow list"
820 );
821
822 domain_config_params.domain_runtime_info = (
824 DEFAULT_EVM_CHAIN_ID + 1,
825 EvmDomainRuntimeConfig {
826 evm_type: EvmType::Public,
827 },
828 )
829 .into();
830
831 Balances::make_free_balance_be(
833 &creator,
834 <Test as Config>::DomainInstantiationDeposit::get()
835 + 1_000_000 * AI3
837 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
838 );
839
840 let domain_id =
842 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
843 .unwrap();
844 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
845
846 assert_eq!(domain_obj.owner_account_id, creator);
847 assert_eq!(domain_obj.created_at, created_at);
848 assert_eq!(
849 domain_obj.domain_config,
850 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
851 );
852 assert_eq!(
853 domain_obj
854 .domain_runtime_info
855 .initial_contract_creation_allow_list(),
856 None,
857 "public EVMs do not have a contract creation allow list"
858 );
859
860 let mut list = vec![];
862 domain_config_params.domain_runtime_info = (
863 DEFAULT_EVM_CHAIN_ID + 2,
864 EvmDomainRuntimeConfig {
865 evm_type: EvmType::Private {
866 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
867 list.clone(),
868 ),
869 },
870 },
871 )
872 .into();
873
874 Balances::make_free_balance_be(
876 &creator,
877 <Test as Config>::DomainInstantiationDeposit::get()
878 + 1_000_000 * AI3
880 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
881 );
882
883 let domain_id =
885 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
886 .unwrap();
887 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
888
889 assert_eq!(domain_obj.owner_account_id, creator);
890 assert_eq!(domain_obj.created_at, created_at);
891 assert_eq!(
892 domain_obj.domain_config,
893 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
894 );
895 assert_eq!(
896 domain_obj
897 .domain_runtime_info
898 .initial_contract_creation_allow_list(),
899 Some(&PermissionedActionAllowedBy::Accounts(list)),
900 "empty list should work"
901 );
902
903 list = vec![hex!("0102030405060708091011121314151617181920").into()];
905 domain_config_params.domain_runtime_info = (
906 DEFAULT_EVM_CHAIN_ID + 3,
907 EvmDomainRuntimeConfig {
908 evm_type: EvmType::Private {
909 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
910 list.clone(),
911 ),
912 },
913 },
914 )
915 .into();
916
917 Balances::make_free_balance_be(
919 &creator,
920 <Test as Config>::DomainInstantiationDeposit::get()
921 + 1_000_000 * AI3
923 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
924 );
925
926 let domain_id =
928 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
929 .unwrap();
930 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
931
932 assert_eq!(domain_obj.owner_account_id, creator);
933 assert_eq!(domain_obj.created_at, created_at);
934 assert_eq!(
935 domain_obj.domain_config,
936 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
937 );
938 assert_eq!(
939 domain_obj
940 .domain_runtime_info
941 .initial_contract_creation_allow_list(),
942 Some(&PermissionedActionAllowedBy::Accounts(list)),
943 "1 account list should work"
944 );
945
946 list = vec![
948 hex!("0102030405060708091011121314151617181920").into(),
949 hex!("1102030405060708091011121314151617181920").into(),
950 hex!("2102030405060708091011121314151617181920").into(),
951 ];
952 domain_config_params.domain_runtime_info = (
953 DEFAULT_EVM_CHAIN_ID + 4,
954 EvmDomainRuntimeConfig {
955 evm_type: EvmType::Private {
956 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
957 list.clone(),
958 ),
959 },
960 },
961 )
962 .into();
963
964 Balances::make_free_balance_be(
966 &creator,
967 <Test as Config>::DomainInstantiationDeposit::get()
968 + 1_000_000 * AI3
970 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
971 );
972
973 let domain_id =
975 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
976 .unwrap();
977 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
978
979 assert_eq!(domain_obj.owner_account_id, creator);
980 assert_eq!(domain_obj.created_at, created_at);
981 assert_eq!(
982 domain_obj.domain_config,
983 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
984 );
985 assert_eq!(
986 domain_obj
987 .domain_runtime_info
988 .initial_contract_creation_allow_list(),
989 Some(&PermissionedActionAllowedBy::Accounts(list)),
990 "multi account list should work"
991 );
992 });
993 }
994}