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 into_complete_raw_genesis, BalanceOf, Config, DomainHashingFor, DomainRegistry,
12 DomainSudoCalls, ExecutionReceiptOf, HoldIdentifier, NextDomainId, RuntimeRegistry,
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::{ensure, PalletError};
23use frame_system::pallet_prelude::*;
24use parity_scale_codec::{Decode, Encode};
25use scale_info::TypeInfo;
26use sp_core::Get;
27use sp_domains::{
28 calculate_max_bundle_weight_and_size, derive_domain_block_hash, DomainBundleLimit, DomainId,
29 DomainRuntimeConfig, DomainSudoCall, DomainsDigestItem, DomainsTransfersTracker,
30 OnDomainInstantiated, OperatorAllowList, RuntimeId, RuntimeType,
31};
32use sp_runtime::traits::{CheckedAdd, Zero};
33use sp_runtime::DigestItem;
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::{new_test_ext, Test};
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;
397 use subspace_runtime_primitives::SSC;
398
399 type Balances = pallet_balances::Pallet<Test>;
400
401 #[test]
402 fn test_domain_instantiation() {
403 let creator = 1u128;
404 let created_at = 0u64;
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 ..Default::default()
474 },
475 created_at: Default::default(),
476 updated_at: Default::default(),
477 instance_count: 0,
478 },
479 );
480
481 assert_eq!(
483 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
484 Err(Error::ExceedMaxDomainBlockSize)
485 );
486 domain_config_params
488 .maybe_bundle_limit
489 .as_mut()
490 .unwrap()
491 .max_bundle_size = 1;
492
493 assert_eq!(
495 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
496 Err(Error::ExceedMaxDomainBlockWeight)
497 );
498 domain_config_params
500 .maybe_bundle_limit
501 .as_mut()
502 .unwrap()
503 .max_bundle_weight = Weight::from_parts(1, 0);
504
505 assert_eq!(
507 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
508 Err(Error::InsufficientFund)
509 );
510 Balances::make_free_balance_be(
512 &creator,
513 <Test as Config>::DomainInstantiationDeposit::get()
514 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
515 );
516 domain_config_params.maybe_bundle_limit = None;
518
519 let domain_id =
521 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
522 .unwrap();
523 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
524
525 assert_eq!(domain_obj.owner_account_id, creator);
526 assert_eq!(domain_obj.created_at, created_at);
527 assert_eq!(
528 domain_obj.domain_config,
529 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
530 );
531 assert_eq!(NextDomainId::<Test>::get(), 1.into());
532 assert_eq!(Balances::usable_balance(creator), Zero::zero());
534
535 let runtime_obj =
537 RuntimeRegistry::<Test>::get(domain_config_params.runtime_id).unwrap();
538 assert_eq!(runtime_obj.instance_count, 1);
539
540 assert_eq!(
542 do_instantiate_domain::<Test>(domain_config_params, creator, created_at),
543 Err(Error::InsufficientFund)
544 );
545
546 let updated_operator_allow_list =
548 OperatorAllowList::Operators(BTreeSet::from_iter(vec![1, 2, 3]));
549 assert_ok!(do_update_domain_allow_list::<Test>(
550 creator,
551 domain_id,
552 updated_operator_allow_list.clone()
553 ));
554 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
555 assert_eq!(
556 domain_obj.domain_config.operator_allow_list,
557 updated_operator_allow_list
558 );
559 });
560 }
561
562 #[test]
563 fn test_domain_instantiation_evm_accounts() {
564 let creator = 1u128;
565 let created_at = 0u64;
566 let mut domain_config_params = DomainConfigParams {
568 domain_name: "evm-domain".to_owned(),
569 runtime_id: 0,
570 maybe_bundle_limit: None,
571 bundle_slot_probability: (1, 1),
572 operator_allow_list: OperatorAllowList::Anyone,
573 initial_balances: vec![(MultiAccountId::Raw(vec![0, 1, 2, 3, 4, 5]), 1_000_000 * SSC)],
574 domain_runtime_config: Default::default(),
575 };
576
577 let mut ext = new_test_ext();
578 ext.execute_with(|| {
579 assert_eq!(NextDomainId::<Test>::get(), 0.into());
580 RuntimeRegistry::<Test>::insert(
582 domain_config_params.runtime_id,
583 RuntimeObject {
584 runtime_name: "evm".to_owned(),
585 runtime_type: Default::default(),
586 runtime_upgrades: 0,
587 hash: Default::default(),
588 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
589 version: RuntimeVersion {
590 spec_name: "test".into(),
591 spec_version: 1,
592 impl_version: 1,
593 transaction_version: 1,
594 ..Default::default()
595 },
596 created_at: Default::default(),
597 updated_at: Default::default(),
598 instance_count: 0,
599 },
600 );
601
602 Balances::make_free_balance_be(
604 &creator,
605 <Test as Config>::DomainInstantiationDeposit::get()
606 + 1_000_000 * SSC
608 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
609 );
610
611 assert_err!(
613 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
614 Error::FailedToGenerateRawGenesis(
615 crate::runtime_registry::Error::InvalidAccountIdType
616 )
617 );
618
619 domain_config_params.initial_balances = vec![
621 (
622 AccountId20Converter::convert(AccountId20::from(hex!(
623 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
624 ))),
625 1_000_000 * SSC,
626 ),
627 (
628 AccountId20Converter::convert(AccountId20::from(hex!(
629 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
630 ))),
631 1_000_000 * SSC,
632 ),
633 ];
634
635 assert_err!(
636 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
637 Error::DuplicateInitialAccounts
638 );
639
640 domain_config_params.initial_balances = vec![
642 (
643 AccountId20Converter::convert(AccountId20::from(hex!(
644 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
645 ))),
646 1_000_000 * SSC,
647 ),
648 (
649 AccountId20Converter::convert(AccountId20::from(hex!(
650 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cbc"
651 ))),
652 1_000_000 * SSC,
653 ),
654 (
655 AccountId20Converter::convert(AccountId20::from(hex!(
656 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566ccc"
657 ))),
658 1_000_000 * SSC,
659 ),
660 (
661 AccountId20Converter::convert(AccountId20::from(hex!(
662 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cdc"
663 ))),
664 1_000_000 * SSC,
665 ),
666 (
667 AccountId20Converter::convert(AccountId20::from(hex!(
668 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cec"
669 ))),
670 1_000_000 * SSC,
671 ),
672 (
673 AccountId20Converter::convert(AccountId20::from(hex!(
674 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cfc"
675 ))),
676 1_000_000 * SSC,
677 ),
678 ];
679
680 assert_err!(
681 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
682 Error::MaxInitialDomainAccounts
683 );
684
685 domain_config_params.initial_balances = vec![(
687 AccountId20Converter::convert(AccountId20::from(hex!(
688 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
689 ))),
690 1,
691 )];
692
693 assert_err!(
694 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at),
695 Error::MinInitialAccountBalance
696 );
697
698 domain_config_params.initial_balances = vec![(
699 AccountId20Converter::convert(AccountId20::from(hex!(
700 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
701 ))),
702 1_000_000 * SSC,
703 )];
704
705 Balances::make_free_balance_be(
707 &creator,
708 <Test as Config>::DomainInstantiationDeposit::get()
709 + 1_000_000 * SSC
711 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
712 );
713
714 let domain_id =
716 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
717 .unwrap();
718 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
719
720 assert_eq!(domain_obj.owner_account_id, creator);
721 assert_eq!(domain_obj.created_at, created_at);
722 assert_eq!(
723 domain_obj.domain_config,
724 into_domain_config::<Test>(domain_config_params).unwrap()
725 );
726 });
727 }
728
729 #[test]
730 fn test_domain_instantiation_evm_contract_allow_list() {
731 let creator = 1u128;
732 let created_at = 0u64;
733 let mut domain_config_params = DomainConfigParams {
735 domain_name: "evm-domain".to_owned(),
736 runtime_id: 0,
737 maybe_bundle_limit: None,
738 bundle_slot_probability: (1, 1),
739 operator_allow_list: OperatorAllowList::Anyone,
740 initial_balances: vec![(
741 AccountId20Converter::convert(AccountId20::from(hex!(
742 "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
743 ))),
744 1_000_000 * SSC,
745 )],
746 domain_runtime_config: Default::default(),
747 };
748
749 let mut ext = new_test_ext();
750 ext.execute_with(|| {
751 assert_eq!(NextDomainId::<Test>::get(), 0.into());
752 RuntimeRegistry::<Test>::insert(
754 domain_config_params.runtime_id,
755 RuntimeObject {
756 runtime_name: "evm".to_owned(),
757 runtime_type: Default::default(),
758 runtime_upgrades: 0,
759 hash: Default::default(),
760 raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]),
761 version: RuntimeVersion {
762 spec_name: "test".into(),
763 spec_version: 1,
764 impl_version: 1,
765 transaction_version: 1,
766 ..Default::default()
767 },
768 created_at: Default::default(),
769 updated_at: Default::default(),
770 instance_count: 0,
771 },
772 );
773
774 Balances::make_free_balance_be(
776 &creator,
777 <Test as Config>::DomainInstantiationDeposit::get()
778 + 1_000_000 * SSC
780 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
781 );
782
783 let domain_id =
785 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
786 .unwrap();
787 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
788
789 assert_eq!(domain_obj.owner_account_id, creator);
790 assert_eq!(domain_obj.created_at, created_at);
791 assert_eq!(
792 domain_obj.domain_config,
793 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
794 );
795 assert_eq!(
796 domain_obj
797 .domain_runtime_info
798 .domain_runtime_config()
799 .initial_contract_creation_allow_list(),
800 None,
801 "default is public EVM, which does not have a contract creation allow list"
802 );
803
804 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
806 evm_type: EvmType::Public,
807 }
808 .into();
809
810 Balances::make_free_balance_be(
812 &creator,
813 <Test as Config>::DomainInstantiationDeposit::get()
814 + 1_000_000 * SSC
816 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
817 );
818
819 let domain_id =
821 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
822 .unwrap();
823 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
824
825 assert_eq!(domain_obj.owner_account_id, creator);
826 assert_eq!(domain_obj.created_at, created_at);
827 assert_eq!(
828 domain_obj.domain_config,
829 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
830 );
831 assert_eq!(
832 domain_obj
833 .domain_runtime_info
834 .domain_runtime_config()
835 .initial_contract_creation_allow_list(),
836 None,
837 "public EVMs do not have a contract creation allow list"
838 );
839
840 let mut list = vec![];
842 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
843 evm_type: EvmType::Private {
844 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
845 list.clone(),
846 ),
847 },
848 }
849 .into();
850
851 Balances::make_free_balance_be(
853 &creator,
854 <Test as Config>::DomainInstantiationDeposit::get()
855 + 1_000_000 * SSC
857 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
858 );
859
860 let domain_id =
862 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
863 .unwrap();
864 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
865
866 assert_eq!(domain_obj.owner_account_id, creator);
867 assert_eq!(domain_obj.created_at, created_at);
868 assert_eq!(
869 domain_obj.domain_config,
870 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
871 );
872 assert_eq!(
873 domain_obj
874 .domain_runtime_info
875 .domain_runtime_config()
876 .initial_contract_creation_allow_list(),
877 Some(&PermissionedActionAllowedBy::Accounts(list)),
878 "empty list should work"
879 );
880
881 list = vec![hex!("0102030405060708091011121314151617181920").into()];
883 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
884 evm_type: EvmType::Private {
885 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
886 list.clone(),
887 ),
888 },
889 }
890 .into();
891
892 Balances::make_free_balance_be(
894 &creator,
895 <Test as Config>::DomainInstantiationDeposit::get()
896 + 1_000_000 * SSC
898 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
899 );
900
901 let domain_id =
903 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
904 .unwrap();
905 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
906
907 assert_eq!(domain_obj.owner_account_id, creator);
908 assert_eq!(domain_obj.created_at, created_at);
909 assert_eq!(
910 domain_obj.domain_config,
911 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
912 );
913 assert_eq!(
914 domain_obj
915 .domain_runtime_info
916 .domain_runtime_config()
917 .initial_contract_creation_allow_list(),
918 Some(&PermissionedActionAllowedBy::Accounts(list)),
919 "1 account list should work"
920 );
921
922 list = vec![
924 hex!("0102030405060708091011121314151617181920").into(),
925 hex!("1102030405060708091011121314151617181920").into(),
926 hex!("2102030405060708091011121314151617181920").into(),
927 ];
928 domain_config_params.domain_runtime_config = EvmDomainRuntimeConfig {
929 evm_type: EvmType::Private {
930 initial_contract_creation_allow_list: PermissionedActionAllowedBy::Accounts(
931 list.clone(),
932 ),
933 },
934 }
935 .into();
936
937 Balances::make_free_balance_be(
939 &creator,
940 <Test as Config>::DomainInstantiationDeposit::get()
941 + 1_000_000 * SSC
943 + <Test as pallet_balances::Config>::ExistentialDeposit::get(),
944 );
945
946 let domain_id =
948 do_instantiate_domain::<Test>(domain_config_params.clone(), creator, created_at)
949 .unwrap();
950 let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();
951
952 assert_eq!(domain_obj.owner_account_id, creator);
953 assert_eq!(domain_obj.created_at, created_at);
954 assert_eq!(
955 domain_obj.domain_config,
956 into_domain_config::<Test>(domain_config_params.clone()).unwrap()
957 );
958 assert_eq!(
959 domain_obj
960 .domain_runtime_info
961 .domain_runtime_config()
962 .initial_contract_creation_allow_list(),
963 Some(&PermissionedActionAllowedBy::Accounts(list)),
964 "multi account list should work"
965 );
966 });
967 }
968}