Skip to content

Commit

Permalink
Merge pull request #1704 from subspace/genesis-config
Browse files Browse the repository at this point in the history
Add genesis config to the genesis domain
  • Loading branch information
NingLin-P authored Aug 1, 2023
2 parents f20d450 + c30d744 commit 42489e3
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 250 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 31 additions & 32 deletions crates/pallet-domains/src/domain_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use scale_info::TypeInfo;
use sp_core::Get;
use sp_domains::domain::generate_genesis_state_root;
use sp_domains::{
DomainId, DomainInstanceData, DomainsDigestItem, GenesisDomain, ReceiptHash, RuntimeId,
RuntimeType,
DomainId, DomainInstanceData, DomainsDigestItem, ReceiptHash, RuntimeId, RuntimeType,
};
use sp_runtime::traits::{CheckedAdd, Zero};
use sp_runtime::DigestItem;
Expand Down Expand Up @@ -56,22 +55,6 @@ pub struct DomainConfig {
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, AccountId> {
/// The address of the domain creator, used to validate updating the domain config.
Expand All @@ -82,6 +65,12 @@ pub struct DomainObject<Number, AccountId> {
pub genesis_receipt_hash: ReceiptHash,
/// The domain config.
pub domain_config: DomainConfig,
/// The genesis config of the domain, encoded in json format.
//
/// NOTE: the WASM code in the `system-pallet` genesis config should be empty to avoid
/// redundancy with the `RuntimeRegistry`. Currently, this value only set to `Some` for
/// the genesis domain instance.
pub raw_genesis_config: Option<Vec<u8>>,
}

pub(crate) fn can_instantiate_domain<T: Config>(
Expand Down Expand Up @@ -130,6 +119,7 @@ pub(crate) fn do_instantiate_domain<T: Config>(
domain_config: DomainConfig,
owner_account_id: T::AccountId,
created_at: T::BlockNumber,
raw_genesis_config: Option<Vec<u8>>,
) -> Result<DomainId, Error> {
can_instantiate_domain::<T>(&owner_account_id, &domain_config)?;

Expand All @@ -138,7 +128,12 @@ pub(crate) fn do_instantiate_domain<T: Config>(
let genesis_receipt = {
let runtime_obj = RuntimeRegistry::<T>::get(domain_config.runtime_id)
.expect("Runtime object must exist as checked in `can_instantiate_domain`; qed");
initialize_genesis_receipt::<T>(domain_id, runtime_obj.runtime_type, runtime_obj.code)?
initialize_genesis_receipt::<T>(
domain_id,
runtime_obj.runtime_type,
runtime_obj.code,
raw_genesis_config.clone(),
)?
};
let genesis_receipt_hash = genesis_receipt.hash();

Expand All @@ -147,6 +142,7 @@ pub(crate) fn do_instantiate_domain<T: Config>(
created_at,
genesis_receipt_hash,
domain_config,
raw_genesis_config,
};
DomainRegistry::<T>::insert(domain_id, domain_obj);

Expand Down Expand Up @@ -183,13 +179,15 @@ fn initialize_genesis_receipt<T: Config>(
domain_id: DomainId,
runtime_type: RuntimeType,
runtime_code: Vec<u8>,
raw_genesis_config: Option<Vec<u8>>,
) -> Result<ExecutionReceiptOf<T>, Error> {
let consensus_genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero());
let genesis_state_root = generate_genesis_state_root(
domain_id,
DomainInstanceData {
runtime_type,
runtime_code,
raw_genesis_config,
},
)
.ok_or(Error::FailedToGenerateGenesisStateRoot)?;
Expand Down Expand Up @@ -236,15 +234,15 @@ mod tests {

// Failed to instantiate domain due to `domain_name` too long
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
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),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::RuntimeNotFound)
);
// Register runtime id
Expand All @@ -270,59 +268,59 @@ mod tests {

// Failed to instantiate domain due to exceed max domain block size limit
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
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),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
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),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::InvalidBundlesPerBlock)
);
domain_config.target_bundles_per_block = u32::MAX;
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
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),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::InvalidSlotProbability)
);
domain_config.bundle_slot_probability = (1, 0);
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::InvalidSlotProbability)
);
domain_config.bundle_slot_probability = (0, 1);
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::InvalidSlotProbability)
);
domain_config.bundle_slot_probability = (2, 1);
assert_eq!(
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
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),
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None),
Err(Error::InsufficientFund)
);
// Set enough fund to creator
Expand All @@ -334,7 +332,8 @@ mod tests {

// `instantiate_domain` must success now
let domain_id =
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at).unwrap();
do_instantiate_domain::<Test>(domain_config.clone(), creator, created_at, None)
.unwrap();
let domain_obj = DomainRegistry::<Test>::get(domain_id).unwrap();

assert_eq!(domain_obj.owner_account_id, creator);
Expand All @@ -346,7 +345,7 @@ mod tests {

// cannot use the locked funds to create a new domain instance
assert!(
do_instantiate_domain::<Test>(domain_config, creator, created_at)
do_instantiate_domain::<Test>(domain_config, creator, created_at, None)
== Err(Error::InsufficientFund)
)
});
Expand Down
34 changes: 23 additions & 11 deletions crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ mod pallet {
let who = ensure_signed(origin)?;
let created_at = frame_system::Pallet::<T>::current_block_number();

let domain_id = do_instantiate_domain::<T>(domain_config, who, created_at)
let domain_id = do_instantiate_domain::<T>(domain_config, who, created_at, None)
.map_err(Error::<T>::from)?;

Self::deposit_event(Event::DomainInstantiated { domain_id });
Expand Down Expand Up @@ -933,23 +933,34 @@ mod pallet {
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {
fn on_initialize(block_number: T::BlockNumber) -> Weight {
if block_number.is_one() {
if let Some(ref genesis_domain) = PendingGenesisDomain::<T>::take() {
if let Some(genesis_domain) = PendingGenesisDomain::<T>::take() {
// Register the genesis domain runtime
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(),
genesis_domain.runtime_name,
genesis_domain.runtime_type,
genesis_domain.runtime_version,
genesis_domain.code,
One::one(),
)
.expect("Genesis runtime registration must always succeed");

// Instantiate the genesis domain
let domain_config = DomainConfig::from_genesis::<T>(genesis_domain, runtime_id);
let domain_owner = genesis_domain.owner_account_id.clone();
let domain_id =
do_instantiate_domain::<T>(domain_config, domain_owner.clone(), One::one())
.expect("Genesis domain instantiation must always succeed");
let domain_config = DomainConfig {
domain_name: genesis_domain.domain_name,
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,
};
let domain_owner = genesis_domain.owner_account_id;
let domain_id = do_instantiate_domain::<T>(
domain_config,
domain_owner.clone(),
One::one(),
Some(genesis_domain.raw_genesis_config),
)
.expect("Genesis domain instantiation must always succeed");

// Register domain_owner as the genesis operator.
let operator_config = OperatorConfig {
Expand Down Expand Up @@ -1080,6 +1091,7 @@ impl<T: Config> Pallet<T> {
DomainInstanceData {
runtime_type,
runtime_code,
raw_genesis_config: domain_obj.raw_genesis_config,
},
domain_obj.created_at,
))
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ fn test_bundle_fromat_verification() {
created_at: Default::default(),
genesis_receipt_hash: Default::default(),
domain_config,
raw_genesis_config: None,
};
DomainRegistry::<Test>::insert(domain_id, domain_obj);

Expand Down
6 changes: 6 additions & 0 deletions crates/sp-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ pub struct GenesisDomain<AccountId> {
pub max_block_weight: Weight,
pub bundle_slot_probability: (u64, u64),
pub target_bundles_per_block: u32,
pub raw_genesis_config: Vec<u8>,

// Genesis operator
pub signing_key: OperatorPublicKey,
Expand Down Expand Up @@ -530,6 +531,11 @@ impl DomainsDigestItem for DigestItem {
pub struct DomainInstanceData {
pub runtime_type: RuntimeType,
pub runtime_code: Vec<u8>,
// The genesis config of the domain, encoded in json format.
//
// NOTE: the WASM code in the `system-pallet` genesis config should be empty to avoid
// redundancy with the `runtime_code` field.
pub raw_genesis_config: Option<Vec<u8>>,
}

impl PassBy for DomainInstanceData {
Expand Down
15 changes: 15 additions & 0 deletions crates/subspace-node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use crate::chain_spec_utils::{
chain_spec_properties, get_account_id_from_seed, get_public_key_from_seed,
};
use crate::domain::evm_chain_spec::{self, SpecId};
use sc_service::{ChainType, NoExtension};
use sc_subspace_chain_specs::ConsensusChainSpec;
use sc_telemetry::TelemetryEndpoints;
Expand Down Expand Up @@ -137,6 +138,7 @@ pub fn gemini_3e_compiled() -> Result<ConsensusChainSpec<GenesisConfig>, String>
})
.collect::<Vec<_>>();
subspace_genesis_config(
SpecId::Gemini,
WASM_BINARY.expect("Wasm binary must be built for Gemini"),
sudo_account,
balances,
Expand Down Expand Up @@ -230,6 +232,7 @@ pub fn devnet_config_compiled() -> Result<ConsensusChainSpec<GenesisConfig>, Str
})
.collect::<Vec<_>>();
subspace_genesis_config(
SpecId::DevNet,
WASM_BINARY.expect("Wasm binary must be built for Gemini"),
sudo_account,
balances,
Expand Down Expand Up @@ -272,6 +275,7 @@ pub fn dev_config() -> Result<ConsensusChainSpec<GenesisConfig>, String> {
ChainType::Development,
|| {
subspace_genesis_config(
SpecId::Dev,
wasm_binary,
// Sudo account
get_account_id_from_seed("Alice"),
Expand Down Expand Up @@ -318,6 +322,7 @@ pub fn local_config() -> Result<ConsensusChainSpec<GenesisConfig>, String> {
ChainType::Local,
|| {
subspace_genesis_config(
SpecId::Local,
wasm_binary,
// Sudo account
get_account_id_from_seed("Alice"),
Expand Down Expand Up @@ -363,6 +368,7 @@ pub fn local_config() -> Result<ConsensusChainSpec<GenesisConfig>, String> {

/// Configure initial storage state for FRAME modules.
fn subspace_genesis_config(
spec_id: SpecId,
wasm_binary: &[u8],
sudo_account: AccountId,
balances: Vec<(AccountId, Balance)>,
Expand All @@ -379,6 +385,14 @@ fn subspace_genesis_config(
confirmation_depth_k,
} = genesis_params;

let raw_domain_genesis_config = {
let mut domain_genesis_config = evm_chain_spec::get_testnet_genesis_by_spec_id(spec_id);
// Clear the WASM code of the genesis config since it is duplicated with `GenesisDomain::code`
domain_genesis_config.system.code = Default::default();
serde_json::to_vec(&domain_genesis_config)
.expect("Genesis config serialization never fails; qed")
};

GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
Expand Down Expand Up @@ -417,6 +431,7 @@ fn subspace_genesis_config(
max_block_weight: MaxDomainBlockWeight::get(),
bundle_slot_probability: (1, 1),
target_bundles_per_block: 10,
raw_genesis_config: raw_domain_genesis_config,

// TODO: Configurable genesis operator signing key.
signing_key: get_public_key_from_seed::<OperatorPublicKey>("Alice"),
Expand Down
11 changes: 10 additions & 1 deletion crates/subspace-node/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,19 @@ where
let DomainInstanceData {
runtime_type,
runtime_code,
raw_genesis_config,
} = domain_instance_data;
let domain_genesis_block_builder = match runtime_type {
RuntimeType::Evm => {
let mut runtime_cfg = evm_domain_runtime::RuntimeGenesisConfig::default();
let mut runtime_cfg = match raw_genesis_config {
Some(raw_genesis_config) => serde_json::from_slice(&raw_genesis_config)
.map_err(|_| {
sp_blockchain::Error::Application(Box::from(
"Failed to deserialize genesis config of the evm domain",
))
})?,
None => evm_domain_runtime::RuntimeGenesisConfig::default(),
};
runtime_cfg.system.code = runtime_code;
runtime_cfg.self_domain_id.domain_id = Some(domain_id);
GenesisBlockBuilder::new(
Expand Down
Loading

0 comments on commit 42489e3

Please sign in to comment.