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