-
Notifications
You must be signed in to change notification settings - Fork 242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Domain registry for domains v2 #1614
Changes from 10 commits
e834db5
c6a0ae1
82db2c8
e0eed54
a94da1a
473fdcf
56ff72b
44ecc6b
087aabc
296507c
71b4487
465b4bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
//! Domain registry for domains | ||
|
||
use crate::{Config, DomainRegistry, NextDomainId, RuntimeRegistry}; | ||
use codec::{Decode, Encode}; | ||
use frame_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons}; | ||
use frame_support::weights::Weight; | ||
use frame_support::{ensure, PalletError}; | ||
use scale_info::TypeInfo; | ||
use sp_core::Get; | ||
use sp_domains::{DomainId, GenesisDomain, RuntimeId}; | ||
use sp_runtime::traits::CheckedAdd; | ||
use sp_std::vec::Vec; | ||
|
||
const DOMAIN_INSTANCE_ID: LockIdentifier = *b"domains "; | ||
|
||
pub type EpochIndex = u32; | ||
|
||
/// Domain registry specific errors | ||
#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] | ||
pub enum Error { | ||
InvalidBundlesPerBlock, | ||
ExceedMaxDomainBlockWeight, | ||
ExceedMaxDomainBlockSize, | ||
MaxDomainId, | ||
InvalidSlotProbability, | ||
RuntimeNotFound, | ||
InsufficientFund, | ||
DomainNameTooLong, | ||
} | ||
|
||
#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] | ||
pub struct DomainConfig { | ||
/// A user defined name for this domain, should be a human-readable UTF-8 encoded string. | ||
pub domain_name: Vec<u8>, | ||
/// A pointer to the `RuntimeRegistry` entry for this domain. | ||
pub runtime_id: RuntimeId, | ||
/// The max block size for this domain, may not exceed the system-wide `MaxDomainBlockSize` limit. | ||
pub max_block_size: u32, | ||
/// The max block weight for this domain, may not exceed the system-wide `MaxDomainBlockWeight` limit. | ||
pub max_block_weight: Weight, | ||
/// The probability of successful bundle in a slot (active slots coefficient). This defines the | ||
/// expected bundle production rate, must be `> 0` and `≤ 1`. | ||
pub bundle_slot_probability: (u64, u64), | ||
/// The expected number of bundles for a domain block, must be `≥ 1` and `≤ MaxBundlesPerBlock`. | ||
pub target_bundles_per_block: u32, | ||
} | ||
|
||
impl DomainConfig { | ||
pub(crate) fn from_genesis<T: Config>( | ||
genesis_domain: &GenesisDomain<T::AccountId>, | ||
runtime_id: RuntimeId, | ||
) -> Self { | ||
DomainConfig { | ||
domain_name: genesis_domain.domain_name.clone(), | ||
runtime_id, | ||
max_block_size: genesis_domain.max_block_size, | ||
max_block_weight: genesis_domain.max_block_weight, | ||
bundle_slot_probability: genesis_domain.bundle_slot_probability, | ||
target_bundles_per_block: genesis_domain.target_bundles_per_block, | ||
} | ||
} | ||
} | ||
|
||
#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] | ||
pub struct DomainObject<Number, Hash, AccountId> { | ||
/// The address of the domain creator, used to validate updating the domain config. | ||
pub owner_account_id: AccountId, | ||
/// The consensus chain block number when the domain first instantiated. | ||
pub created_at: Number, | ||
/// The hash of the genesis execution receipt for this domain. | ||
pub genesis_receipt_hash: Hash, | ||
/// The domain config. | ||
pub domain_config: DomainConfig, | ||
} | ||
|
||
pub(crate) fn can_instantiate_domain<T: Config>( | ||
owner_account_id: &T::AccountId, | ||
domain_config: &DomainConfig, | ||
) -> Result<(), Error> { | ||
ensure!( | ||
domain_config.domain_name.len() as u32 <= T::MaxDomainNameLength::get(), | ||
Error::DomainNameTooLong, | ||
); | ||
ensure!( | ||
RuntimeRegistry::<T>::contains_key(domain_config.runtime_id), | ||
Error::RuntimeNotFound | ||
); | ||
ensure!( | ||
domain_config.max_block_size <= T::MaxDomainBlockSize::get(), | ||
Error::ExceedMaxDomainBlockSize | ||
); | ||
ensure!( | ||
domain_config.max_block_weight.ref_time() <= T::MaxDomainBlockWeight::get().ref_time(), | ||
Error::ExceedMaxDomainBlockWeight | ||
); | ||
ensure!( | ||
domain_config.target_bundles_per_block != 0 | ||
&& domain_config.target_bundles_per_block <= T::MaxBundlesPerBlock::get(), | ||
Error::InvalidBundlesPerBlock | ||
); | ||
|
||
// `bundle_slot_probability` must be `> 0` and `≤ 1` | ||
let (numerator, denominator) = domain_config.bundle_slot_probability; | ||
ensure!( | ||
numerator != 0 && denominator != 0 && numerator <= denominator, | ||
Error::InvalidSlotProbability | ||
); | ||
|
||
ensure!( | ||
T::Currency::free_balance(owner_account_id) >= T::DomainInstantiationDeposit::get(), | ||
Error::InsufficientFund | ||
); | ||
|
||
Ok(()) | ||
} | ||
|
||
pub(crate) fn do_instantiate_domain<T: Config>( | ||
domain_config: DomainConfig, | ||
owner_account_id: T::AccountId, | ||
created_at: T::BlockNumber, | ||
) -> Result<DomainId, Error> { | ||
can_instantiate_domain::<T>(&owner_account_id, &domain_config)?; | ||
|
||
// Lock up fund of the domain instance creator | ||
T::Currency::set_lock( | ||
DOMAIN_INSTANCE_ID, | ||
&owner_account_id, | ||
T::DomainInstantiationDeposit::get(), | ||
WithdrawReasons::all(), | ||
); | ||
|
||
let domain_obj = DomainObject { | ||
owner_account_id, | ||
created_at, | ||
// TODO: drive the `genesis_receipt_hash` from genesis config through host function | ||
vedhavyas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
genesis_receipt_hash: T::Hash::default(), | ||
domain_config, | ||
}; | ||
let domain_id = NextDomainId::<T>::get(); | ||
DomainRegistry::<T>::insert(domain_id, domain_obj); | ||
|
||
let next_domain_id = domain_id.checked_add(&1.into()).ok_or(Error::MaxDomainId)?; | ||
NextDomainId::<T>::set(next_domain_id); | ||
|
||
// TODO: initialize the stake summary for this domain | ||
|
||
// TODO: initialize the genesis block in the domain block tree once we can drive the | ||
// genesis ER from genesis config through host function | ||
Comment on lines
+147
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The genesis block here is referring to the block in the block tree, not the actual domain block. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's obviously confusing, I know the confusion essentially comes from the block tree though 🤷♂️ |
||
|
||
Ok(domain_id) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::pallet::{DomainRegistry, NextDomainId, RuntimeRegistry}; | ||
use crate::runtime_registry::RuntimeObject; | ||
use crate::tests::{new_test_ext, Test}; | ||
use sp_version::RuntimeVersion; | ||
|
||
#[test] | ||
fn test_domain_instantiation() { | ||
let creator = 1u64; | ||
let created_at = 0u64; | ||
// Construct an invalid domain config initially | ||
let mut domain_config = DomainConfig { | ||
domain_name: vec![0; 1024], | ||
runtime_id: 0, | ||
max_block_size: u32::MAX, | ||
max_block_weight: Weight::MAX, | ||
bundle_slot_probability: (0, 0), | ||
target_bundles_per_block: 0, | ||
}; | ||
|
||
let mut ext = new_test_ext(); | ||
ext.execute_with(|| { | ||
assert_eq!(NextDomainId::<Test>::get(), 0.into()); | ||
|
||
// Failed to instantiate domain due to `domain_name` too long | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::DomainNameTooLong) | ||
); | ||
// Recorrect `domain_name` | ||
domain_config.domain_name = b"evm-domain".to_vec(); | ||
|
||
// Failed to instantiate domain due to using unregistered runtime id | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::RuntimeNotFound) | ||
); | ||
// Register runtime id | ||
RuntimeRegistry::<Test>::insert( | ||
domain_config.runtime_id, | ||
RuntimeObject { | ||
runtime_name: b"evm".to_vec(), | ||
runtime_type: Default::default(), | ||
runtime_upgrades: 0, | ||
hash: Default::default(), | ||
code: vec![1, 2, 3, 4], | ||
version: RuntimeVersion { | ||
spec_name: "test".into(), | ||
spec_version: 1, | ||
impl_version: 1, | ||
transaction_version: 1, | ||
..Default::default() | ||
}, | ||
created_at: Default::default(), | ||
updated_at: Default::default(), | ||
}, | ||
); | ||
|
||
// Failed to instantiate domain due to exceed max domain block size limit | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::ExceedMaxDomainBlockSize) | ||
); | ||
// Recorrect `max_block_size` | ||
domain_config.max_block_size = 1; | ||
|
||
// Failed to instantiate domain due to exceed max domain block weight limit | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::ExceedMaxDomainBlockWeight) | ||
); | ||
// Recorrect `max_block_weight` | ||
domain_config.max_block_weight = Weight::from_parts(1, 0); | ||
|
||
// Failed to instantiate domain due to invalid `target_bundles_per_block` | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidBundlesPerBlock) | ||
); | ||
domain_config.target_bundles_per_block = u32::MAX; | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidBundlesPerBlock) | ||
); | ||
// Recorrect `target_bundles_per_block` | ||
domain_config.target_bundles_per_block = 1; | ||
|
||
// Failed to instantiate domain due to invalid `bundle_slot_probability` | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidSlotProbability) | ||
); | ||
domain_config.bundle_slot_probability = (1, 0); | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidSlotProbability) | ||
); | ||
domain_config.bundle_slot_probability = (0, 1); | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidSlotProbability) | ||
); | ||
domain_config.bundle_slot_probability = (2, 1); | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InvalidSlotProbability) | ||
); | ||
// Recorrect `bundle_slot_probability` | ||
domain_config.bundle_slot_probability = (1, 1); | ||
|
||
// Failed to instantiate domain due to creator don't have enough fund | ||
assert_eq!( | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at), | ||
Err(Error::InsufficientFund) | ||
); | ||
// Set enough fund to creator | ||
<Test as Config>::Currency::make_free_balance_be( | ||
&creator, | ||
<Test as Config>::DomainInstantiationDeposit::get(), | ||
); | ||
|
||
// `instantiate_domain` must success now | ||
let domain_id = | ||
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at).unwrap(); | ||
let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap(); | ||
|
||
assert_eq!(domain_obj.owner_account_id, creator); | ||
assert_eq!(domain_obj.created_at, created_at); | ||
assert_eq!(domain_obj.domain_config, domain_config); | ||
assert_eq!(NextDomainId::<Test>::get(), 1.into()); | ||
// Fund locked up thus can't withdraw | ||
assert!(<Test as Config>::Currency::ensure_can_withdraw( | ||
&creator, | ||
1, | ||
WithdrawReasons::all(), | ||
<Test as Config>::DomainInstantiationDeposit::get() - 1 | ||
) | ||
.is_err()); | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there a BoundedVec for this purpose ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the
domain_name
is constructed by the user not by the runtime, thus we still need to check it manually, and after the domain is instantiated,domain_name
is not supposed to change.