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::mock::Test;
402 use crate::tests::{TEST_RUNTIME_APIS, new_test_ext};
403 use domain_runtime_primitives::{AccountId20, AccountId20Converter, DEFAULT_EVM_CHAIN_ID};
404 use frame_support::traits::Currency;
405 use frame_support::{assert_err, assert_ok};
406 use hex_literal::hex;
407 use sp_domains::storage::RawGenesis;
408 use sp_domains::{EvmDomainRuntimeConfig, EvmType, PermissionedActionAllowedBy, RuntimeObject};
409 use sp_runtime::traits::Convert;
410 use sp_std::vec;
411 use sp_version::{RuntimeVersion, create_apis_vec};
412 use subspace_runtime_primitives::AI3;
413
414 type Balances = pallet_balances::Pallet<Test>;
415
416 #[test]
417 fn test_domain_instantiation() {
418 let creator = 1u128;
419 let created_at = 0u32;
420 let mut domain_config_params = DomainConfigParams {
422 domain_name: String::from_utf8(vec![0; 1024]).unwrap(),
423 runtime_id: 0,
424 maybe_bundle_limit: Some(DomainBundleLimit {
425 max_bundle_size: u32::MAX,
426 max_bundle_weight: Weight::MAX,
427 }),
428 bundle_slot_probability: (0, 0),
429 operator_allow_list: OperatorAllowList::Anyone,
430 initial_balances: Default::default(),
431 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
432 };
433
434 let mut ext = new_test_ext();
435 ext.execute_with(|| {
436 assert_eq!(NextDomainId::<Test>::get(), 0.into());
437
438 assert_eq!(
440 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
441 Err(Error::InvalidSlotProbability)
442 );
443 domain_config_params.bundle_slot_probability = (1, 0);
444 assert_eq!(
445 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
446 Err(Error::InvalidSlotProbability)
447 );
448 domain_config_params.bundle_slot_probability = (0, 1);
449 assert_eq!(
450 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
451 Err(Error::InvalidSlotProbability)
452 );
453 domain_config_params.bundle_slot_probability = (2, 1);
454 assert_eq!(
455 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
456 Err(Error::InvalidSlotProbability)
457 );
458 domain_config_params.bundle_slot_probability = (1, 1);
460
461 assert_eq!(
463 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
464 Err(Error::DomainNameTooLong)
465 );
466 "evm-domain".clone_into(&mut domain_config_params.domain_name);
468
469 assert_eq!(
471 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
472 Err(Error::RuntimeNotFound)
473 );
474 RuntimeRegistry::<Test>::insert(
476 domain_config_params.runtime_id,
477 RuntimeObject {
478 runtime_name: "evm".to_owned(),
479 runtime_type: Default::default(),
480 runtime_upgrades: 0,
481 hash: Default::default(),
482 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
483 version: RuntimeVersion {
484 spec_name: "test".into(),
485 spec_version: 1,
486 impl_version: 1,
487 transaction_version: 1,
488 apis: create_apis_vec!(TEST_RUNTIME_APIS),
489 ..Default::default()
490 },
491 created_at: Default::default(),
492 updated_at: Default::default(),
493 instance_count: 0,
494 },
495 );
496
497 assert_eq!(
499 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
500 Err(Error::ExceedMaxDomainBlockSize)
501 );
502 domain_config_params
504 .maybe_bundle_limit
505 .as_mut()
506 .unwrap()
507 .max_bundle_size = 1;
508
509 assert_eq!(
511 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
512 Err(Error::ExceedMaxDomainBlockWeight)
513 );
514 domain_config_params
516 .maybe_bundle_limit
517 .as_mut()
518 .unwrap()
519 .max_bundle_weight = Weight::from_parts(1, 0);
520
521 assert_eq!(
523 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
524 Err(Error::InsufficientFund)
525 );
526 Balances::make_free_balance_be(
528 &creator,
529 <Test as Config>::DomainInstantiationDeposit::get()
530 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
531 );
532 domain_config_params.maybe_bundle_limit = None;
534
535 let domain_id =
537 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
538 .unwrap();
539 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
540
541 assert_eq!(domain_obj.owner_account_id, creator);
542 assert_eq!(domain_obj.created_at, created_at);
543 assert_eq!(
544 domain_obj.domain_config,
545 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
546 );
547 assert_eq!(NextDomainId::<Test>::get(), 1.into());
548 assert_eq!(Balances::usable_balance(creator), Zero::zero());
550
551 let runtime_obj =
553 RuntimeRegistry::<Test>::get(domain_config_params.runtime_id).unwrap();
554 assert_eq!(runtime_obj.instance_count, 1);
555
556 assert_eq!(
558 do_instantiate_domain::<Test>(domain_config_params, creator, created_at),
559 Err(Error::InsufficientFund)
560 );
561
562 let updated_operator_allow_list =
564 OperatorAllowList::Operators(BTreeSet::from_iter(vec![1, 2, 3]));
565 assert_ok!(do_update_domain_allow_list::<Test>(
566 creator,
567 domain_id,
568 updated_operator_allow_list.clone()
569 ));
570 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
571 assert_eq!(
572 domain_obj.domain_config.operator_allow_list,
573 updated_operator_allow_list
574 );
575 });
576 }
577
578 #[test]
579 fn test_domain_instantiation_evm_accounts() {
580 let creator = 1u128;
581 let created_at = 0u32;
582 let mut domain_config_params = DomainConfigParams {
584 domain_name: "evm-domain".to_owned(),
585 runtime_id: 0,
586 maybe_bundle_limit: None,
587 bundle_slot_probability: (1, 1),
588 operator_allow_list: OperatorAllowList::Anyone,
589 initial_balances: vec![(MultiAccountId::Raw(vec![0, 1, 2, 3, 4, 5]), 1_000_000 * AI3)],
590 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
591 };
592
593 let mut ext = new_test_ext();
594 ext.execute_with(|| {
595 assert_eq!(NextDomainId::<Test>::get(), 0.into());
596 RuntimeRegistry::<Test>::insert(
598 domain_config_params.runtime_id,
599 RuntimeObject {
600 runtime_name: "evm".to_owned(),
601 runtime_type: Default::default(),
602 runtime_upgrades: 0,
603 hash: Default::default(),
604 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
605 version: RuntimeVersion {
606 spec_name: "test".into(),
607 spec_version: 1,
608 impl_version: 1,
609 transaction_version: 1,
610 apis: create_apis_vec!(TEST_RUNTIME_APIS),
611 ..Default::default()
612 },
613 created_at: Default::default(),
614 updated_at: Default::default(),
615 instance_count: 0,
616 },
617 );
618
619 Balances::make_free_balance_be(
621 &creator,
622 <Test as Config>::DomainInstantiationDeposit::get()
623 + 1_000_000 * AI3
625 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
626 );
627
628 assert_err!(
630 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
631 Error::FailedToGenerateRawGenesis(
632 crate::runtime_registry::Error::InvalidAccountIdType
633 )
634 );
635
636 domain_config_params.initial_balances = vec![
638 (
639 AccountId20Converter::convert(AccountId20::from(hex!(
640 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
641 ))),
642 1_000_000 * AI3,
643 ),
644 (
645 AccountId20Converter::convert(AccountId20::from(hex!(
646 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
647 ))),
648 1_000_000 * AI3,
649 ),
650 ];
651
652 assert_err!(
653 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
654 Error::DuplicateInitialAccounts
655 );
656
657 domain_config_params.initial_balances = vec![
659 (
660 AccountId20Converter::convert(AccountId20::from(hex!(
661 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
662 ))),
663 1_000_000 * AI3,
664 ),
665 (
666 AccountId20Converter::convert(AccountId20::from(hex!(
667 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cbc"
668 ))),
669 1_000_000 * AI3,
670 ),
671 (
672 AccountId20Converter::convert(AccountId20::from(hex!(
673 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566ccc"
674 ))),
675 1_000_000 * AI3,
676 ),
677 (
678 AccountId20Converter::convert(AccountId20::from(hex!(
679 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cdc"
680 ))),
681 1_000_000 * AI3,
682 ),
683 (
684 AccountId20Converter::convert(AccountId20::from(hex!(
685 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cec"
686 ))),
687 1_000_000 * AI3,
688 ),
689 (
690 AccountId20Converter::convert(AccountId20::from(hex!(
691 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cfc"
692 ))),
693 1_000_000 * AI3,
694 ),
695 ];
696
697 assert_err!(
698 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
699 Error::MaxInitialDomainAccounts
700 );
701
702 domain_config_params.initial_balances = vec![(
704 AccountId20Converter::convert(AccountId20::from(hex!(
705 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
706 ))),
707 1,
708 )];
709
710 assert_err!(
711 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
712 Error::MinInitialAccountBalance
713 );
714
715 domain_config_params.initial_balances = vec![(
716 AccountId20Converter::convert(AccountId20::from(hex!(
717 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
718 ))),
719 1_000_000 * AI3,
720 )];
721
722 Balances::make_free_balance_be(
724 &creator,
725 <Test as Config>::DomainInstantiationDeposit::get()
726 + 1_000_000 * AI3
728 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
729 );
730
731 domain_config_params.domain_runtime_info =
733 (DEFAULT_EVM_CHAIN_ID + 1, Default::default()).into();
734 let domain_id =
735 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
736 .unwrap();
737 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
738
739 assert_eq!(domain_obj.owner_account_id, creator);
740 assert_eq!(domain_obj.created_at, created_at);
741 assert_eq!(
742 domain_obj.domain_config,
743 into_domain_config::<Test>(domain_config_params).unwrap()
744 );
745 });
746 }
747
748 #[test]
749 fn test_domain_instantiation_evm_contract_allow_list() {
750 let creator = 1u128;
751 let created_at = 0u32;
752 let mut domain_config_params = DomainConfigParams {
754 domain_name: "evm-domain".to_owned(),
755 runtime_id: 0,
756 maybe_bundle_limit: None,
757 bundle_slot_probability: (1, 1),
758 operator_allow_list: OperatorAllowList::Anyone,
759 initial_balances: vec![(
760 AccountId20Converter::convert(AccountId20::from(hex!(
761 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
762 ))),
763 1_000_000 * AI3,
764 )],
765 domain_runtime_info: (DEFAULT_EVM_CHAIN_ID, Default::default()).into(),
766 };
767
768 let mut ext = new_test_ext();
769 ext.execute_with(|| {
770 assert_eq!(NextDomainId::<Test>::get(), 0.into());
771 RuntimeRegistry::<Test>::insert(
773 domain_config_params.runtime_id,
774 RuntimeObject {
775 runtime_name: "evm".to_owned(),
776 runtime_type: Default::default(),
777 runtime_upgrades: 0,
778 hash: Default::default(),
779 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
780 version: RuntimeVersion {
781 spec_name: "test".into(),
782 spec_version: 1,
783 impl_version: 1,
784 transaction_version: 1,
785 apis: create_apis_vec!(TEST_RUNTIME_APIS),
786 ..Default::default()
787 },
788 created_at: Default::default(),
789 updated_at: Default::default(),
790 instance_count: 0,
791 },
792 );
793
794 Balances::make_free_balance_be(
796 &creator,
797 <Test as Config>::DomainInstantiationDeposit::get()
798 + 1_000_000 * AI3
800 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
801 );
802
803 let domain_id =
805 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
806 .unwrap();
807 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
808
809 assert_eq!(domain_obj.owner_account_id, creator);
810 assert_eq!(domain_obj.created_at, created_at);
811 assert_eq!(
812 domain_obj.domain_config,
813 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
814 );
815 assert_eq!(
816 domain_obj
817 .domain_runtime_info
818 .initial_contract_creation_allow_list(),
819 None,
820 "default is public EVM, which does not have a contract creation allow list"
821 );
822
823 domain_config_params.domain_runtime_info = (
825 DEFAULT_EVM_CHAIN_ID + 1,
826 EvmDomainRuntimeConfig {
827 evm_type: EvmType::Public,
828 },
829 )
830 .into();
831
832 Balances::make_free_balance_be(
834 &creator,
835 <Test as Config>::DomainInstantiationDeposit::get()
836 + 1_000_000 * AI3
838 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
839 );
840
841 let domain_id =
843 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
844 .unwrap();
845 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
846
847 assert_eq!(domain_obj.owner_account_id, creator);
848 assert_eq!(domain_obj.created_at, created_at);
849 assert_eq!(
850 domain_obj.domain_config,
851 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
852 );
853 assert_eq!(
854 domain_obj
855 .domain_runtime_info
856 .initial_contract_creation_allow_list(),
857 None,
858 "public EVMs do not have a contract creation allow list"
859 );
860
861 let mut list = vec![];
863 domain_config_params.domain_runtime_info = (
864 DEFAULT_EVM_CHAIN_ID + 2,
865 EvmDomainRuntimeConfig {
866 evm_type: EvmType::Private {
867 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
868 list.clone(),
869 ),
870 },
871 },
872 )
873 .into();
874
875 Balances::make_free_balance_be(
877 &creator,
878 <Test as Config>::DomainInstantiationDeposit::get()
879 + 1_000_000 * AI3
881 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
882 );
883
884 let domain_id =
886 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
887 .unwrap();
888 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
889
890 assert_eq!(domain_obj.owner_account_id, creator);
891 assert_eq!(domain_obj.created_at, created_at);
892 assert_eq!(
893 domain_obj.domain_config,
894 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
895 );
896 assert_eq!(
897 domain_obj
898 .domain_runtime_info
899 .initial_contract_creation_allow_list(),
900 Some(&PermissionedActionAllowedBy::Accounts(list)),
901 "empty list should work"
902 );
903
904 list = vec![hex!("0102030405060708091011121314151617181920").into()];
906 domain_config_params.domain_runtime_info = (
907 DEFAULT_EVM_CHAIN_ID + 3,
908 EvmDomainRuntimeConfig {
909 evm_type: EvmType::Private {
910 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
911 list.clone(),
912 ),
913 },
914 },
915 )
916 .into();
917
918 Balances::make_free_balance_be(
920 &creator,
921 <Test as Config>::DomainInstantiationDeposit::get()
922 + 1_000_000 * AI3
924 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
925 );
926
927 let domain_id =
929 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
930 .unwrap();
931 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
932
933 assert_eq!(domain_obj.owner_account_id, creator);
934 assert_eq!(domain_obj.created_at, created_at);
935 assert_eq!(
936 domain_obj.domain_config,
937 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
938 );
939 assert_eq!(
940 domain_obj
941 .domain_runtime_info
942 .initial_contract_creation_allow_list(),
943 Some(&PermissionedActionAllowedBy::Accounts(list)),
944 "1 account list should work"
945 );
946
947 list = vec![
949 hex!("0102030405060708091011121314151617181920").into(),
950 hex!("1102030405060708091011121314151617181920").into(),
951 hex!("2102030405060708091011121314151617181920").into(),
952 ];
953 domain_config_params.domain_runtime_info = (
954 DEFAULT_EVM_CHAIN_ID + 4,
955 EvmDomainRuntimeConfig {
956 evm_type: EvmType::Private {
957 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
958 list.clone(),
959 ),
960 },
961 },
962 )
963 .into();
964
965 Balances::make_free_balance_be(
967 &creator,
968 <Test as Config>::DomainInstantiationDeposit::get()
969 + 1_000_000 * AI3
971 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
972 );
973
974 let domain_id =
976 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
977 .unwrap();
978 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
979
980 assert_eq!(domain_obj.owner_account_id, creator);
981 assert_eq!(domain_obj.created_at, created_at);
982 assert_eq!(
983 domain_obj.domain_config,
984 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
985 );
986 assert_eq!(
987 domain_obj
988 .domain_runtime_info
989 .initial_contract_creation_allow_list(),
990 Some(&PermissionedActionAllowedBy::Accounts(list)),
991 "multi account list should work"
992 );
993 });
994 }
995}