-
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 5 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,149 @@ | ||
//! 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 genesis config. | ||
pub domain_config: DomainConfig, | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
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::MaxDomainBlockSize::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> { | ||
let domain_obj = DomainObject { | ||
owner_account_id: owner_account_id.clone(), | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
|
||
// Lock up fund of the domain instance creator | ||
T::Currency::set_lock( | ||
DOMAIN_INSTANCE_ID, | ||
&owner_account_id, | ||
T::DomainInstantiationDeposit::get(), | ||
WithdrawReasons::all(), | ||
); | ||
|
||
// 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) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,10 +24,11 @@ mod benchmarking; | |
#[cfg(test)] | ||
mod tests; | ||
|
||
pub mod domain_registry; | ||
pub mod runtime_registry; | ||
pub mod weights; | ||
|
||
use frame_support::traits::Get; | ||
use frame_support::traits::{Currency, Get}; | ||
use frame_system::offchain::SubmitTransaction; | ||
pub use pallet::*; | ||
use sp_core::H256; | ||
|
@@ -38,16 +39,25 @@ use sp_runtime::transaction_validity::TransactionValidityError; | |
use sp_std::vec::Vec; | ||
use subspace_core_primitives::U256; | ||
|
||
/// The balance type used by the currency system. | ||
pub type BalanceOf<T> = | ||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; | ||
|
||
#[frame_support::pallet] | ||
mod pallet { | ||
use crate::calculate_tx_range; | ||
use crate::domain_registry::{ | ||
can_instantiate_domain, do_instantiate_domain, DomainConfig, DomainObject, | ||
Error as DomainRegistryError, | ||
}; | ||
use crate::runtime_registry::{ | ||
do_register_runtime, do_schedule_runtime_upgrade, do_upgrade_runtimes, | ||
register_runtime_at_genesis, Error as RuntimeRegistryError, RuntimeObject, | ||
ScheduledRuntimeUpgrade, | ||
}; | ||
use crate::weights::WeightInfo; | ||
use crate::{calculate_tx_range, BalanceOf}; | ||
use frame_support::pallet_prelude::{StorageMap, *}; | ||
use frame_support::traits::LockableCurrency; | ||
use frame_support::weights::Weight; | ||
use frame_support::{Identity, PalletError}; | ||
use frame_system::pallet_prelude::*; | ||
|
@@ -56,7 +66,7 @@ mod pallet { | |
use sp_domains::fraud_proof::FraudProof; | ||
use sp_domains::transaction::InvalidTransactionCode; | ||
use sp_domains::{ | ||
DomainId, ExecutorPublicKey, GenesisDomainRuntime, OpaqueBundle, RuntimeId, RuntimeType, | ||
DomainId, ExecutorPublicKey, GenesisDomain, OpaqueBundle, RuntimeId, RuntimeType, | ||
}; | ||
use sp_runtime::traits::{BlockNumberProvider, Zero}; | ||
use sp_std::fmt::Debug; | ||
|
@@ -73,6 +83,29 @@ mod pallet { | |
/// Delay before a domain runtime is upgraded. | ||
type DomainRuntimeUpgradeDelay: Get<Self::BlockNumber>; | ||
|
||
/// The maximum block size limit for all domain. | ||
#[pallet::constant] | ||
type MaxDomainBlockSize: Get<u32>; | ||
|
||
/// The maximum block weight limit for all domain. | ||
#[pallet::constant] | ||
type MaxDomainBlockWeight: Get<Weight>; | ||
|
||
/// The maximum bundle per block limit for all domain. | ||
#[pallet::constant] | ||
type MaxBundlesPerBlock: Get<u32>; | ||
|
||
/// The maximum domain name length limit for all domain. | ||
#[pallet::constant] | ||
type MaxDomainNameLength: Get<u32>; | ||
|
||
/// The amount of fund to be locked up for the domain instance creator. | ||
#[pallet::constant] | ||
type DomainInstantiationDeposit: Get<BalanceOf<Self>>; | ||
|
||
/// The currency trait. | ||
type Currency: LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>; | ||
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. I just realize that we shouldn't continue using 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. Okay, will resolve it in my upcoming PR. |
||
|
||
/// Weight information for extrinsics in this pallet. | ||
type WeightInfo: WeightInfo; | ||
|
||
|
@@ -113,6 +146,20 @@ mod pallet { | |
OptionQuery, | ||
>; | ||
|
||
/// Stores the next domain id. | ||
#[pallet::storage] | ||
pub(super) type NextDomainId<T> = StorageValue<_, DomainId, ValueQuery>; | ||
|
||
/// The domain registry | ||
#[pallet::storage] | ||
pub(super) type DomainRegistry<T: Config> = StorageMap< | ||
_, | ||
Identity, | ||
DomainId, | ||
DomainObject<T::BlockNumber, T::Hash, T::AccountId>, | ||
OptionQuery, | ||
>; | ||
|
||
#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] | ||
pub enum BundleError { | ||
/// The signer of bundle is unexpected. | ||
|
@@ -179,6 +226,12 @@ mod pallet { | |
} | ||
} | ||
|
||
impl<T> From<DomainRegistryError> for Error<T> { | ||
fn from(err: DomainRegistryError) -> Self { | ||
Error::DomainRegistry(err) | ||
} | ||
} | ||
|
||
#[pallet::error] | ||
pub enum Error<T> { | ||
/// Can not find the block hash of given primary block number. | ||
|
@@ -189,6 +242,8 @@ mod pallet { | |
FraudProof(FraudProofError), | ||
/// Runtime registry specific errors | ||
RuntimeRegistry(RuntimeRegistryError), | ||
/// Domain registry specific errors | ||
DomainRegistry(DomainRegistryError), | ||
} | ||
|
||
#[pallet::event] | ||
|
@@ -211,6 +266,10 @@ mod pallet { | |
DomainRuntimeUpgraded { | ||
runtime_id: RuntimeId, | ||
}, | ||
DomainInstantiated { | ||
domain_id: DomainId, | ||
runtime_id: RuntimeId, | ||
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. not sure if the 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. Runtime event is used to send notifications about changes or conditions in the runtime to external entities like users, chain explorers, or dApps. I think 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. Not sure I agree here. Events should signal that domain was created with a specific DomainId. Rest of the information should be fetched from the state as required. If we add IMO, events should hold as minimum details as possible but still points to bigger information on the state 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. Okay, I don't have strong opinion here, the |
||
}, | ||
} | ||
|
||
/// Per-domain state for tx range calculation. | ||
|
@@ -364,27 +423,72 @@ mod pallet { | |
|
||
Ok(()) | ||
} | ||
|
||
#[pallet::call_index(4)] | ||
#[pallet::weight((Weight::from_all(10_000), Pays::Yes))] | ||
// TODO: proper benchmark | ||
pub fn instantiate_domain( | ||
origin: OriginFor<T>, | ||
domain_config: DomainConfig, | ||
) -> DispatchResult { | ||
let who = ensure_signed(origin)?; | ||
|
||
can_instantiate_domain::<T>(&who, &domain_config).map_err(Error::<T>::from)?; | ||
|
||
let runtime_id = domain_config.runtime_id; | ||
let created_at = frame_system::Pallet::<T>::current_block_number(); | ||
|
||
let domain_id = do_instantiate_domain::<T>(domain_config, who, created_at) | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.map_err(Error::<T>::from)?; | ||
|
||
Self::deposit_event(Event::DomainInstantiated { | ||
domain_id, | ||
runtime_id, | ||
}); | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
#[pallet::genesis_config] | ||
#[derive(Default)] | ||
pub struct GenesisConfig { | ||
pub genesis_domain_runtime: Option<GenesisDomainRuntime>, | ||
pub struct GenesisConfig<T: Config> { | ||
pub genesis_domain: Option<GenesisDomain<T::AccountId>>, | ||
} | ||
|
||
impl<T: Config> Default for GenesisConfig<T> { | ||
fn default() -> Self { | ||
GenesisConfig { | ||
genesis_domain: None, | ||
} | ||
} | ||
} | ||
|
||
#[pallet::genesis_build] | ||
impl<T: Config> GenesisBuild<T> for GenesisConfig { | ||
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> { | ||
fn build(&self) { | ||
if let Some(genesis_domain_runtime) = &self.genesis_domain_runtime { | ||
if let Some(genesis_domain) = &self.genesis_domain { | ||
// Register the genesis domain runtime | ||
register_runtime_at_genesis::<T>( | ||
genesis_domain_runtime.name.clone(), | ||
genesis_domain_runtime.runtime_type.clone(), | ||
genesis_domain_runtime.runtime_version.clone(), | ||
genesis_domain_runtime.code.clone(), | ||
let runtime_id = register_runtime_at_genesis::<T>( | ||
genesis_domain.runtime_name.clone(), | ||
genesis_domain.runtime_type.clone(), | ||
genesis_domain.runtime_version.clone(), | ||
genesis_domain.code.clone(), | ||
Zero::zero(), | ||
) | ||
.expect("Genesis runtime registration must always succeed"); | ||
|
||
// Instantiate the genesis domain | ||
let domain_config = DomainConfig::from_genesis::<T>(genesis_domain, runtime_id); | ||
can_instantiate_domain::<T>(&genesis_domain.owner_account_id, &domain_config) | ||
.expect("Genesis domain config must be valid"); | ||
|
||
let created_at = frame_system::Pallet::<T>::current_block_number(); | ||
do_instantiate_domain::<T>( | ||
domain_config, | ||
genesis_domain.owner_account_id.clone(), | ||
created_at, | ||
) | ||
.expect("Genesis domain instantiation must always succeed"); | ||
} | ||
} | ||
} | ||
|
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.