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 T::CurrentBundleAndExecutionReceiptVersion::get().execution_receipt_version,
321 )
322 };
323 let genesis_receipt_hash = genesis_receipt.hash::<DomainHashingFor<T>>();
324
325 let domain_obj = DomainObject {
326 owner_account_id: owner_account_id.clone(),
327 created_at,
328 genesis_receipt_hash,
329 domain_config,
330 domain_runtime_info,
331 domain_instantiation_deposit,
332 };
333 DomainRegistry::<T>::insert(domain_id, domain_obj);
334
335 let next_domain_id = domain_id.checked_add(&1.into()).ok_or(Error::MaxDomainId)?;
336 NextDomainId::<T>::set(next_domain_id);
337
338 T::Currency::hold(
340 &T::HoldIdentifier::domain_instantiation_id(),
341 &owner_account_id,
342 domain_instantiation_deposit,
343 )
344 .map_err(|_| Error::BalanceFreeze)?;
345
346 DomainStakingSummary::<T>::insert(
347 domain_id,
348 StakingSummary {
349 current_epoch_index: 0,
350 current_total_stake: Zero::zero(),
351 current_operators: BTreeMap::new(),
352 next_operators: BTreeSet::new(),
353 current_epoch_rewards: BTreeMap::new(),
354 },
355 );
356
357 import_genesis_receipt::<T>(domain_id, genesis_receipt);
358 T::OnDomainInstantiated::on_domain_instantiated(domain_id);
359
360 DomainSudoCalls::<T>::insert(domain_id, DomainSudoCall { maybe_call: None });
361
362 frame_system::Pallet::<T>::deposit_log(DigestItem::domain_instantiation(domain_id));
363
364 Ok(domain_id)
365}
366
367pub(crate) fn do_update_domain_allow_list<T: Config>(
368 domain_owner: T::AccountId,
369 domain_id: DomainId,
370 updated_operator_allow_list: OperatorAllowList<T::AccountId>,
371) -> Result<(), Error> {
372 let mut domain_obj = DomainRegistry::<T>::get(domain_id).ok_or(Error::DomainNotFound)?;
373
374 ensure!(
375 domain_obj.owner_account_id == domain_owner,
376 Error::NotDomainOwner,
377 );
378
379 domain_obj.domain_config.operator_allow_list = updated_operator_allow_list;
380 DomainRegistry::<T>::insert(domain_id, domain_obj);
381
382 Ok(())
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::tests::{TEST_RUNTIME_APIS, Test, new_test_ext};
389 use domain_runtime_primitives::{AccountId20, AccountId20Converter};
390 use frame_support::traits::Currency;
391 use frame_support::{assert_err, assert_ok};
392 use hex_literal::hex;
393 use sp_domains::storage::RawGenesis;
394 use sp_domains::{EvmDomainRuntimeConfig, EvmType, PermissionedActionAllowedBy, RuntimeObject};
395 use sp_runtime::traits::Convert;
396 use sp_std::vec;
397 use sp_version::{RuntimeVersion, create_apis_vec};
398 use subspace_runtime_primitives::AI3;
399
400 type Balances = pallet_balances::Pallet<Test>;
401
402 #[test]
403 fn test_domain_instantiation() {
404 let creator = 1u128;
405 let created_at = 0u32;
406 let mut domain_config_params = DomainConfigParams {
408 domain_name: String::from_utf8(vec![0; 1024]).unwrap(),
409 runtime_id: 0,
410 maybe_bundle_limit: Some(DomainBundleLimit {
411 max_bundle_size: u32::MAX,
412 max_bundle_weight: Weight::MAX,
413 }),
414 bundle_slot_probability: (0, 0),
415 operator_allow_list: OperatorAllowList::Anyone,
416 initial_balances: Default::default(),
417 domain_runtime_config: Default::default(),
418 };
419
420 let mut ext = new_test_ext();
421 ext.execute_with(|| {
422 assert_eq!(NextDomainId::<Test>::get(), 0.into());
423
424 assert_eq!(
426 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
427 Err(Error::InvalidSlotProbability)
428 );
429 domain_config_params.bundle_slot_probability = (1, 0);
430 assert_eq!(
431 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
432 Err(Error::InvalidSlotProbability)
433 );
434 domain_config_params.bundle_slot_probability = (0, 1);
435 assert_eq!(
436 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
437 Err(Error::InvalidSlotProbability)
438 );
439 domain_config_params.bundle_slot_probability = (2, 1);
440 assert_eq!(
441 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
442 Err(Error::InvalidSlotProbability)
443 );
444 domain_config_params.bundle_slot_probability = (1, 1);
446
447 assert_eq!(
449 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
450 Err(Error::DomainNameTooLong)
451 );
452 "evm-domain".clone_into(&mut domain_config_params.domain_name);
454
455 assert_eq!(
457 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
458 Err(Error::RuntimeNotFound)
459 );
460 RuntimeRegistry::<Test>::insert(
462 domain_config_params.runtime_id,
463 RuntimeObject {
464 runtime_name: "evm".to_owned(),
465 runtime_type: Default::default(),
466 runtime_upgrades: 0,
467 hash: Default::default(),
468 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
469 version: RuntimeVersion {
470 spec_name: "test".into(),
471 spec_version: 1,
472 impl_version: 1,
473 transaction_version: 1,
474 apis: create_apis_vec!(TEST_RUNTIME_APIS),
475 ..Default::default()
476 },
477 created_at: Default::default(),
478 updated_at: Default::default(),
479 instance_count: 0,
480 },
481 );
482
483 assert_eq!(
485 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
486 Err(Error::ExceedMaxDomainBlockSize)
487 );
488 domain_config_params
490 .maybe_bundle_limit
491 .as_mut()
492 .unwrap()
493 .max_bundle_size = 1;
494
495 assert_eq!(
497 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
498 Err(Error::ExceedMaxDomainBlockWeight)
499 );
500 domain_config_params
502 .maybe_bundle_limit
503 .as_mut()
504 .unwrap()
505 .max_bundle_weight = Weight::from_parts(1, 0);
506
507 assert_eq!(
509 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
510 Err(Error::InsufficientFund)
511 );
512 Balances::make_free_balance_be(
514 &creator,
515 <Test as Config>::DomainInstantiationDeposit::get()
516 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
517 );
518 domain_config_params.maybe_bundle_limit = None;
520
521 let domain_id =
523 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
524 .unwrap();
525 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
526
527 assert_eq!(domain_obj.owner_account_id, creator);
528 assert_eq!(domain_obj.created_at, created_at);
529 assert_eq!(
530 domain_obj.domain_config,
531 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
532 );
533 assert_eq!(NextDomainId::<Test>::get(), 1.into());
534 assert_eq!(Balances::usable_balance(creator), Zero::zero());
536
537 let runtime_obj =
539 RuntimeRegistry::<Test>::get(domain_config_params.runtime_id).unwrap();
540 assert_eq!(runtime_obj.instance_count, 1);
541
542 assert_eq!(
544 do_instantiate_domain::<Test>(domain_config_params, creator, created_at),
545 Err(Error::InsufficientFund)
546 );
547
548 let updated_operator_allow_list =
550 OperatorAllowList::Operators(BTreeSet::from_iter(vec![1, 2, 3]));
551 assert_ok!(do_update_domain_allow_list::<Test>(
552 creator,
553 domain_id,
554 updated_operator_allow_list.clone()
555 ));
556 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
557 assert_eq!(
558 domain_obj.domain_config.operator_allow_list,
559 updated_operator_allow_list
560 );
561 });
562 }
563
564 #[test]
565 fn test_domain_instantiation_evm_accounts() {
566 let creator = 1u128;
567 let created_at = 0u32;
568 let mut domain_config_params = DomainConfigParams {
570 domain_name: "evm-domain".to_owned(),
571 runtime_id: 0,
572 maybe_bundle_limit: None,
573 bundle_slot_probability: (1, 1),
574 operator_allow_list: OperatorAllowList::Anyone,
575 initial_balances: vec![(MultiAccountId::Raw(vec![0, 1, 2, 3, 4, 5]), 1_000_000 * AI3)],
576 domain_runtime_config: Default::default(),
577 };
578
579 let mut ext = new_test_ext();
580 ext.execute_with(|| {
581 assert_eq!(NextDomainId::<Test>::get(), 0.into());
582 RuntimeRegistry::<Test>::insert(
584 domain_config_params.runtime_id,
585 RuntimeObject {
586 runtime_name: "evm".to_owned(),
587 runtime_type: Default::default(),
588 runtime_upgrades: 0,
589 hash: Default::default(),
590 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
591 version: RuntimeVersion {
592 spec_name: "test".into(),
593 spec_version: 1,
594 impl_version: 1,
595 transaction_version: 1,
596 apis: create_apis_vec!(TEST_RUNTIME_APIS),
597 ..Default::default()
598 },
599 created_at: Default::default(),
600 updated_at: Default::default(),
601 instance_count: 0,
602 },
603 );
604
605 Balances::make_free_balance_be(
607 &creator,
608 <Test as Config>::DomainInstantiationDeposit::get()
609 + 1_000_000 * AI3
611 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
612 );
613
614 assert_err!(
616 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
617 Error::FailedToGenerateRawGenesis(
618 crate::runtime_registry::Error::InvalidAccountIdType
619 )
620 );
621
622 domain_config_params.initial_balances = vec![
624 (
625 AccountId20Converter::convert(AccountId20::from(hex!(
626 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
627 ))),
628 1_000_000 * AI3,
629 ),
630 (
631 AccountId20Converter::convert(AccountId20::from(hex!(
632 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
633 ))),
634 1_000_000 * AI3,
635 ),
636 ];
637
638 assert_err!(
639 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
640 Error::DuplicateInitialAccounts
641 );
642
643 domain_config_params.initial_balances = vec![
645 (
646 AccountId20Converter::convert(AccountId20::from(hex!(
647 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
648 ))),
649 1_000_000 * AI3,
650 ),
651 (
652 AccountId20Converter::convert(AccountId20::from(hex!(
653 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cbc"
654 ))),
655 1_000_000 * AI3,
656 ),
657 (
658 AccountId20Converter::convert(AccountId20::from(hex!(
659 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566ccc"
660 ))),
661 1_000_000 * AI3,
662 ),
663 (
664 AccountId20Converter::convert(AccountId20::from(hex!(
665 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cdc"
666 ))),
667 1_000_000 * AI3,
668 ),
669 (
670 AccountId20Converter::convert(AccountId20::from(hex!(
671 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cec"
672 ))),
673 1_000_000 * AI3,
674 ),
675 (
676 AccountId20Converter::convert(AccountId20::from(hex!(
677 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cfc"
678 ))),
679 1_000_000 * AI3,
680 ),
681 ];
682
683 assert_err!(
684 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
685 Error::MaxInitialDomainAccounts
686 );
687
688 domain_config_params.initial_balances = vec![(
690 AccountId20Converter::convert(AccountId20::from(hex!(
691 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
692 ))),
693 1,
694 )];
695
696 assert_err!(
697 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
698 Error::MinInitialAccountBalance
699 );
700
701 domain_config_params.initial_balances = vec![(
702 AccountId20Converter::convert(AccountId20::from(hex!(
703 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
704 ))),
705 1_000_000 * AI3,
706 )];
707
708 Balances::make_free_balance_be(
710 &creator,
711 <Test as Config>::DomainInstantiationDeposit::get()
712 + 1_000_000 * AI3
714 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
715 );
716
717 let domain_id =
719 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
720 .unwrap();
721 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
722
723 assert_eq!(domain_obj.owner_account_id, creator);
724 assert_eq!(domain_obj.created_at, created_at);
725 assert_eq!(
726 domain_obj.domain_config,
727 into_domain_config::<Test>(domain_config_params).unwrap()
728 );
729 });
730 }
731
732 #[test]
733 fn test_domain_instantiation_evm_contract_allow_list() {
734 let creator = 1u128;
735 let created_at = 0u32;
736 let mut domain_config_params = DomainConfigParams {
738 domain_name: "evm-domain".to_owned(),
739 runtime_id: 0,
740 maybe_bundle_limit: None,
741 bundle_slot_probability: (1, 1),
742 operator_allow_list: OperatorAllowList::Anyone,
743 initial_balances: vec![(
744 AccountId20Converter::convert(AccountId20::from(hex!(
745 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
746 ))),
747 1_000_000 * AI3,
748 )],
749 domain_runtime_config: Default::default(),
750 };
751
752 let mut ext = new_test_ext();
753 ext.execute_with(|| {
754 assert_eq!(NextDomainId::<Test>::get(), 0.into());
755 RuntimeRegistry::<Test>::insert(
757 domain_config_params.runtime_id,
758 RuntimeObject {
759 runtime_name: "evm".to_owned(),
760 runtime_type: Default::default(),
761 runtime_upgrades: 0,
762 hash: Default::default(),
763 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
764 version: RuntimeVersion {
765 spec_name: "test".into(),
766 spec_version: 1,
767 impl_version: 1,
768 transaction_version: 1,
769 apis: create_apis_vec!(TEST_RUNTIME_APIS),
770 ..Default::default()
771 },
772 created_at: Default::default(),
773 updated_at: Default::default(),
774 instance_count: 0,
775 },
776 );
777
778 Balances::make_free_balance_be(
780 &creator,
781 <Test as Config>::DomainInstantiationDeposit::get()
782 + 1_000_000 * AI3
784 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
785 );
786
787 let domain_id =
789 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
790 .unwrap();
791 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
792
793 assert_eq!(domain_obj.owner_account_id, creator);
794 assert_eq!(domain_obj.created_at, created_at);
795 assert_eq!(
796 domain_obj.domain_config,
797 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
798 );
799 assert_eq!(
800 domain_obj
801 .domain_runtime_info
802 .domain_runtime_config()
803 .initial_contract_creation_allow_list(),
804 None,
805 "default is public EVM, which does not have a contract creation allow list"
806 );
807
808 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
810 evm_type: EvmType::Public,
811 }
812 .into();
813
814 Balances::make_free_balance_be(
816 &creator,
817 <Test as Config>::DomainInstantiationDeposit::get()
818 + 1_000_000 * AI3
820 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
821 );
822
823 let domain_id =
825 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
826 .unwrap();
827 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
828
829 assert_eq!(domain_obj.owner_account_id, creator);
830 assert_eq!(domain_obj.created_at, created_at);
831 assert_eq!(
832 domain_obj.domain_config,
833 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
834 );
835 assert_eq!(
836 domain_obj
837 .domain_runtime_info
838 .domain_runtime_config()
839 .initial_contract_creation_allow_list(),
840 None,
841 "public EVMs do not have a contract creation allow list"
842 );
843
844 let mut list = vec![];
846 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
847 evm_type: EvmType::Private {
848 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
849 list.clone(),
850 ),
851 },
852 }
853 .into();
854
855 Balances::make_free_balance_be(
857 &creator,
858 <Test as Config>::DomainInstantiationDeposit::get()
859 + 1_000_000 * AI3
861 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
862 );
863
864 let domain_id =
866 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
867 .unwrap();
868 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
869
870 assert_eq!(domain_obj.owner_account_id, creator);
871 assert_eq!(domain_obj.created_at, created_at);
872 assert_eq!(
873 domain_obj.domain_config,
874 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
875 );
876 assert_eq!(
877 domain_obj
878 .domain_runtime_info
879 .domain_runtime_config()
880 .initial_contract_creation_allow_list(),
881 Some(&PermissionedActionAllowedBy::Accounts(list)),
882 "empty list should work"
883 );
884
885 list = vec![hex!("0102030405060708091011121314151617181920").into()];
887 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
888 evm_type: EvmType::Private {
889 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
890 list.clone(),
891 ),
892 },
893 }
894 .into();
895
896 Balances::make_free_balance_be(
898 &creator,
899 <Test as Config>::DomainInstantiationDeposit::get()
900 + 1_000_000 * AI3
902 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
903 );
904
905 let domain_id =
907 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
908 .unwrap();
909 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
910
911 assert_eq!(domain_obj.owner_account_id, creator);
912 assert_eq!(domain_obj.created_at, created_at);
913 assert_eq!(
914 domain_obj.domain_config,
915 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
916 );
917 assert_eq!(
918 domain_obj
919 .domain_runtime_info
920 .domain_runtime_config()
921 .initial_contract_creation_allow_list(),
922 Some(&PermissionedActionAllowedBy::Accounts(list)),
923 "1 account list should work"
924 );
925
926 list = vec![
928 hex!("0102030405060708091011121314151617181920").into(),
929 hex!("1102030405060708091011121314151617181920").into(),
930 hex!("2102030405060708091011121314151617181920").into(),
931 ];
932 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
933 evm_type: EvmType::Private {
934 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
935 list.clone(),
936 ),
937 },
938 }
939 .into();
940
941 Balances::make_free_balance_be(
943 &creator,
944 <Test as Config>::DomainInstantiationDeposit::get()
945 + 1_000_000 * AI3
947 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
948 );
949
950 let domain_id =
952 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
953 .unwrap();
954 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
955
956 assert_eq!(domain_obj.owner_account_id, creator);
957 assert_eq!(domain_obj.created_at, created_at);
958 assert_eq!(
959 domain_obj.domain_config,
960 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
961 );
962 assert_eq!(
963 domain_obj
964 .domain_runtime_info
965 .domain_runtime_config()
966 .initial_contract_creation_allow_list(),
967 Some(&PermissionedActionAllowedBy::Accounts(list)),
968 "multi account list should work"
969 );
970 });
971 }
972}