diff --git a/crates/configuration/src/parachain.rs b/crates/configuration/src/parachain.rs index 7cdac5befb62a7f968c6d9ac06ffa1515e0a62f4..2647b4579f46f6826953d1b197e25d7b42fef4d2 100644 --- a/crates/configuration/src/parachain.rs +++ b/crates/configuration/src/parachain.rs @@ -7,7 +7,8 @@ use crate::shared::{ helpers::{merge_errors, merge_errors_vecs}, macros::states, node::{self, NodeConfig, NodeConfigBuilder}, - types::{AssetLocation, Chain, ChainDefaultContext, Command, ParaId}, + resources::{Resources, ResourcesBuilder}, + types::{Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ParaId}, }; #[derive(Debug, Clone, PartialEq)] @@ -23,7 +24,7 @@ pub struct ParachainConfig { id: u32, /// Chain to use (use None if you are running adder-collator or undying-collator). - chain: Option<Chain>, + chain: Chain, /// Registration strategy for the parachain. registration_strategy: Option<RegistrationStrategy>, @@ -31,6 +32,16 @@ pub struct ParachainConfig { /// Parachain balance. initial_balance: u128, + default_command: Option<Command>, + + default_image: Option<Image>, + + default_resources: Option<Resources>, + + default_db_snapshot: Option<AssetLocation>, + + default_args: Vec<Arg>, + /// Path to WASM runtime. genesis_wasm_path: Option<AssetLocation>, @@ -61,8 +72,8 @@ impl ParachainConfig { self.id } - pub fn chain(&self) -> Option<&Chain> { - self.chain.as_ref() + pub fn chain(&self) -> &Chain { + &self.chain } pub fn registration_strategy(&self) -> Option<&RegistrationStrategy> { @@ -109,6 +120,7 @@ impl ParachainConfig { states! { Initial, WithId, + WithChain, WithAtLeastOneCollator } @@ -124,9 +136,16 @@ impl Default for ParachainConfigBuilder<Initial> { Self { config: ParachainConfig { id: 100, - chain: None, + chain: "" + .try_into() + .expect("empty string should be valid. this is a bug, please report it: https://github.com/paritytech/zombienet-sdk/issues"), registration_strategy: Some(RegistrationStrategy::InGenesis), initial_balance: 2_000_000_000_000, + default_command: None, + default_image: None, + default_resources: None, + default_db_snapshot: None, + default_args: vec![], genesis_wasm_path: None, genesis_wasm_generator: None, genesis_state_path: None, @@ -153,6 +172,16 @@ impl<A> ParachainConfigBuilder<A> { _state: PhantomData, } } + + fn default_context(&self) -> ChainDefaultContext { + ChainDefaultContext { + default_command: self.config.default_command.clone(), + default_image: self.config.default_image.clone(), + default_resources: self.config.default_resources.clone(), + default_db_snapshot: self.config.default_db_snapshot.clone(), + default_args: self.config.default_args.clone(), + } + } } impl ParachainConfigBuilder<Initial> { @@ -166,67 +195,124 @@ impl ParachainConfigBuilder<Initial> { } impl ParachainConfigBuilder<WithId> { - pub fn with_collator( - self, - f: fn(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>, - ) -> ParachainConfigBuilder<WithAtLeastOneCollator> { - match f(NodeConfigBuilder::new(ChainDefaultContext::default())).build() { - Ok(collator) => Self::transition( + pub fn with_chain<T>(self, chain: T) -> ParachainConfigBuilder<WithChain> + where + T: TryInto<Chain>, + T::Error: Error + Send + Sync + 'static, + { + match chain.try_into() { + Ok(chain) => Self::transition( ParachainConfig { - collators: vec![collator], + chain, ..self.config }, self.errors, ), - Err((name, errors)) => Self::transition( + Err(error) => Self::transition( self.config, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::<Vec<_>>(), - ), + merge_errors(self.errors, FieldError::Chain(error.into()).into()), ), } } } -impl ParachainConfigBuilder<WithAtLeastOneCollator> { - pub fn with_chain<T>(self, chain: T) -> Self +impl ParachainConfigBuilder<WithChain> { + pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { + Self::transition( + ParachainConfig { + registration_strategy: Some(strategy), + ..self.config + }, + self.errors, + ) + } + + pub fn with_initial_balance(self, initial_balance: u128) -> Self { + Self::transition( + ParachainConfig { + initial_balance, + ..self.config + }, + self.errors, + ) + } + + pub fn with_default_command<T>(self, command: T) -> Self where - T: TryInto<Chain>, + T: TryInto<Command>, T::Error: Error + Send + Sync + 'static, { - match chain.try_into() { - Ok(chain) => Self::transition( + match command.try_into() { + Ok(command) => Self::transition( ParachainConfig { - chain: Some(chain), + default_command: Some(command), ..self.config }, self.errors, ), Err(error) => Self::transition( self.config, - merge_errors(self.errors, FieldError::Chain(error.into()).into()), + merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()), ), } } - pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { + pub fn with_default_image<T>(self, image: T) -> Self + where + T: TryInto<Image>, + T::Error: Error + Send + Sync + 'static, + { + match image.try_into() { + Ok(image) => Self::transition( + ParachainConfig { + default_image: Some(image), + ..self.config + }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()), + ), + } + } + + pub fn with_default_resources(self, f: fn(ResourcesBuilder) -> ResourcesBuilder) -> Self { + match f(ResourcesBuilder::new()).build() { + Ok(default_resources) => Self::transition( + ParachainConfig { + default_resources: Some(default_resources), + ..self.config + }, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| FieldError::DefaultResources(error).into()) + .collect::<Vec<_>>(), + ), + ), + } + } + + pub fn with_default_db_snapshot(self, location: impl Into<AssetLocation>) -> Self { Self::transition( ParachainConfig { - registration_strategy: Some(strategy), + default_db_snapshot: Some(location.into()), ..self.config }, self.errors, ) } - pub fn with_initial_balance(self, initial_balance: u128) -> Self { + pub fn with_default_args(self, args: Vec<Arg>) -> Self { Self::transition( ParachainConfig { - initial_balance, + default_args: args, ..self.config }, self.errors, @@ -345,6 +431,33 @@ impl ParachainConfigBuilder<WithAtLeastOneCollator> { ) } + pub fn with_collator( + self, + f: fn(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>, + ) -> ParachainConfigBuilder<WithAtLeastOneCollator> { + match f(NodeConfigBuilder::new(self.default_context())).build() { + Ok(collator) => Self::transition( + ParachainConfig { + collators: vec![collator], + ..self.config + }, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::<Vec<_>>(), + ), + ), + } + } +} + +impl ParachainConfigBuilder<WithAtLeastOneCollator> { pub fn with_collator( self, f: fn(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>, @@ -387,18 +500,6 @@ mod tests { fn parachain_config_builder_should_succeeds_and_returns_a_new_parachain_config() { let parachain_config = ParachainConfigBuilder::new() .with_id(1000) - .with_collator(|collator1| { - collator1 - .with_name("collator1") - .with_command("command1") - .bootnode(true) - }) - .with_collator(|collator2| { - collator2 - .with_name("collator2") - .with_command("command2") - .validator(true) - }) .with_chain("mychainname") .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) .with_initial_balance(100_000_042) @@ -412,6 +513,18 @@ mod tests { "/ip4/10.41.122.55/tcp/45421", "/ip4/51.144.222.10/tcp/2333", ]) + .with_collator(|collator1| { + collator1 + .with_name("collator1") + .with_command("command1") + .bootnode(true) + }) + .with_collator(|collator2| { + collator2 + .with_name("collator2") + .with_command("command2") + .validator(true) + }) .build() .unwrap(); @@ -425,7 +538,7 @@ mod tests { assert_eq!(collator2.name(), "collator2"); assert_eq!(collator2.command().unwrap().as_str(), "command2"); assert!(collator2.is_validator()); - assert_eq!(parachain_config.chain().unwrap().as_str(), "mychainname"); + assert_eq!(parachain_config.chain().as_str(), "mychainname"); assert_eq!( parachain_config.registration_strategy().unwrap(), &RegistrationStrategy::UsingExtrinsic @@ -467,6 +580,7 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(1000) + .with_chain("myparachain") .with_collator(|collator| { collator .with_name("collator") @@ -488,13 +602,13 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(1000) + .with_chain("invalid chain") .with_collator(|collator| { collator .with_name("collator") .with_command("command") .validator(true) }) - .with_chain("invalid chain") .build() .unwrap_err(); @@ -511,13 +625,14 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(2000) + .with_chain("myparachain") + .with_genesis_wasm_generator("invalid command") .with_collator(|collator| { collator .with_name("collator") .with_command("command") .validator(true) }) - .with_genesis_wasm_generator("invalid command") .build() .unwrap_err(); @@ -534,13 +649,14 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(1000) + .with_chain("myparachain") + .with_genesis_state_generator("invalid command") .with_collator(|collator| { collator .with_name("collator") .with_command("command") .validator(true) }) - .with_genesis_state_generator("invalid command") .build() .unwrap_err(); @@ -557,13 +673,14 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(2000) + .with_chain("myparachain") + .with_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) .with_collator(|collator| { collator .with_name("collator") .with_command("command") .validator(true) }) - .with_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) .build() .unwrap_err(); @@ -584,6 +701,7 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(2000) + .with_chain("myparachain") .with_collator(|collator1| { collator1 .with_name("collator1") @@ -613,6 +731,8 @@ mod tests { ) { let (para_id, errors) = ParachainConfigBuilder::new() .with_id(2000) + .with_chain("myparachain") + .with_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) .with_collator(|collator1| { collator1 .with_name("collator1") @@ -632,7 +752,6 @@ mod tests { .with_image("invalid.image") .with_initial_balance(20000000) }) - .with_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) .build() .unwrap_err(); @@ -640,23 +759,24 @@ mod tests { assert_eq!(errors.len(), 5); assert_eq!( errors.get(0).unwrap().to_string(), - "collators['collator1'].command: 'invalid command' shouldn't contains whitespace" + "bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); assert_eq!( errors.get(1).unwrap().to_string(), - r"collators['collator1'].resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'", + "bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " ); assert_eq!( errors.get(2).unwrap().to_string(), - "collators['collator2'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" + "collators['collator1'].command: 'invalid command' shouldn't contains whitespace" ); assert_eq!( errors.get(3).unwrap().to_string(), - "bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" + r"collators['collator1'].resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'", ); assert_eq!( errors.get(4).unwrap().to_string(), - "bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " + "collators['collator2'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); + } }