Newer
Older
use std::{cell::RefCell, error::Error, fmt::Display, marker::PhantomData, rc::Rc};

Loris Moulin
committed
use anyhow::anyhow;

Loris Moulin
committed
use multiaddr::Multiaddr;

Loris Moulin
committed
use crate::{
shared::{
errors::{ConfigError, FieldError},
helpers::{merge_errors, merge_errors_vecs},
node::{self, NodeConfig, NodeConfigBuilder},
resources::{Resources, ResourcesBuilder},
types::{
Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, U128,
},

Loris Moulin
committed
},
utils::{default_as_true, default_initial_balance, is_false},
/// The registration strategy that will be used for the parachain.

Loris Moulin
committed
pub enum RegistrationStrategy {
/// The parachain will be added to the genesis before spawning.

Loris Moulin
committed
InGenesis,
/// The parachain will be registered using an extrinsic after spawning.

Loris Moulin
committed
UsingExtrinsic,
/// The parachaing will not be registered and the user can doit after spawning manually.
Manual,

Loris Moulin
committed
}

Loris Moulin
committed
impl Serialize for RegistrationStrategy {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("RegistrationStrategy", 1)?;
match self {
Self::InGenesis => state.serialize_field("add_to_genesis", &true)?,
Self::UsingExtrinsic => state.serialize_field("register_para", &true)?,
Self::Manual => {
state.serialize_field("add_to_genesis", &false)?;
state.serialize_field("register_para", &false)?;
},

Loris Moulin
committed
}
state.end()
}
}
struct RegistrationStrategyVisitor;
impl<'de> Visitor<'de> for RegistrationStrategyVisitor {
type Value = RegistrationStrategy;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct RegistrationStrategy")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
{
let mut add_to_genesis = false;
let mut register_para = false;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"addToGenesis" | "add_to_genesis" => add_to_genesis = map.next_value()?,
"registerPara" | "register_para" => register_para = map.next_value()?,
return Err(de::Error::unknown_field(
&key,
&["add_to_genesis", "register_para"],
match (add_to_genesis, register_para) {
(true, false) => Ok(RegistrationStrategy::InGenesis),
(false, true) => Ok(RegistrationStrategy::UsingExtrinsic),
_ => Err(de::Error::missing_field("add_to_genesis or register_para")),
}
}
}
impl<'de> Deserialize<'de> for RegistrationStrategy {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
deserializer.deserialize_struct(
"RegistrationStrategy",
&["add_to_genesis", "register_para"],
/// A parachain configuration, composed of collators and fine-grained configuration options.
id: u32,

Loris Moulin
committed
chain: Option<Chain>,

Loris Moulin
committed
#[serde(flatten)]

Loris Moulin
committed
registration_strategy: Option<RegistrationStrategy>,
#[serde(
skip_serializing_if = "super::utils::is_true",
default = "default_as_true"
)]

Loris Moulin
committed
onboard_as_parachain: bool,
#[serde(rename = "balance", default = "default_initial_balance")]

Loris Moulin
committed
initial_balance: U128,
default_command: Option<Command>,
default_image: Option<Image>,
default_resources: Option<Resources>,
default_db_snapshot: Option<AssetLocation>,
#[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
default_args: Vec<Arg>,

Loris Moulin
committed
genesis_wasm_path: Option<AssetLocation>,
genesis_wasm_generator: Option<Command>,

Loris Moulin
committed
genesis_state_path: Option<AssetLocation>,
genesis_state_generator: Option<Command>,

Loris Moulin
committed
chain_spec_path: Option<AssetLocation>,
// Full _template_ command, will be rendered using [tera]
// and executed for generate the chain-spec.
// available tokens {{chainName}} / {{disableBootnodes}}
chain_spec_command: Option<String>,
// Does the chain_spec_command needs to be run locally
#[serde(skip_serializing_if = "is_false", default)]
chain_spec_command_is_local: bool,
#[serde(rename = "cumulus_based", default = "default_as_true")]
is_cumulus_based: bool,
#[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]

Loris Moulin
committed
bootnodes_addresses: Vec<Multiaddr>,
genesis_overrides: Option<serde_json::Value>,
#[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
pub(crate) collators: Vec<NodeConfig>,
// Single collator config, added for backward compatibility
// with `toml` networks definitions from v1.
// This field can only be set loading an old `toml` definition
// with `[parachain.collator]` key.
// NOTE: if the file also contains multiple collators defined in
// `[[parachain.collators]], the single configuration will be added to the bottom.
collator: Option<NodeConfig>,

Loris Moulin
committed
impl ParachainConfig {
pub fn id(&self) -> u32 {

Loris Moulin
committed
self.id
}

Loris Moulin
committed
pub fn chain(&self) -> Option<&Chain> {
self.chain.as_ref()

Loris Moulin
committed
}
/// The registration strategy for the parachain.

Loris Moulin
committed
pub fn registration_strategy(&self) -> Option<&RegistrationStrategy> {
self.registration_strategy.as_ref()
}

Loris Moulin
committed
/// Whether the parachain should be onboarded or stay a parathread
pub fn onboard_as_parachain(&self) -> bool {
self.onboard_as_parachain
}
/// The initial balance of the parachain account.

Loris Moulin
committed
pub fn initial_balance(&self) -> u128 {

Loris Moulin
committed
self.initial_balance.0

Loris Moulin
committed
}
/// The default command used for collators.

Loris Moulin
committed
pub fn default_command(&self) -> Option<&Command> {
self.default_command.as_ref()
}
/// The default container image used for collators.

Loris Moulin
committed
pub fn default_image(&self) -> Option<&Image> {
self.default_image.as_ref()
}
/// The default resources limits used for collators.

Loris Moulin
committed
pub fn default_resources(&self) -> Option<&Resources> {
self.default_resources.as_ref()
}
/// The default database snapshot location that will be used for state.

Loris Moulin
committed
pub fn default_db_snapshot(&self) -> Option<&AssetLocation> {
self.default_db_snapshot.as_ref()
}
/// The default arguments that will be used to execute the collator command.

Loris Moulin
committed
pub fn default_args(&self) -> Vec<&Arg> {
self.default_args.iter().collect::<Vec<&Arg>>()
}
/// The location of a pre-existing genesis WASM runtime blob of the parachain.

Loris Moulin
committed
pub fn genesis_wasm_path(&self) -> Option<&AssetLocation> {
self.genesis_wasm_path.as_ref()
}
/// The generator command used to create the genesis WASM runtime blob of the parachain.
pub fn genesis_wasm_generator(&self) -> Option<&Command> {
self.genesis_wasm_generator.as_ref()

Loris Moulin
committed
}
/// The location of a pre-existing genesis state of the parachain.

Loris Moulin
committed
pub fn genesis_state_path(&self) -> Option<&AssetLocation> {
self.genesis_state_path.as_ref()
}
/// The generator command used to create the genesis state of the parachain.
pub fn genesis_state_generator(&self) -> Option<&Command> {
self.genesis_state_generator.as_ref()

Loris Moulin
committed
}
/// The genesis overrides as a JSON value.
pub fn genesis_overrides(&self) -> Option<&serde_json::Value> {
self.genesis_overrides.as_ref()
}
/// The location of a pre-existing chain specification for the parachain.

Loris Moulin
committed
pub fn chain_spec_path(&self) -> Option<&AssetLocation> {
self.chain_spec_path.as_ref()
}
/// The full _template_ command to genera the chain-spec
pub fn chain_spec_command(&self) -> Option<&str> {
self.chain_spec_command.as_deref()
}
/// Does the chain_spec_command needs to be run locally
pub fn chain_spec_command_is_local(&self) -> bool {
self.chain_spec_command_is_local
}
/// Whether the parachain is based on cumulus.

Loris Moulin
committed
pub fn is_cumulus_based(&self) -> bool {
self.is_cumulus_based

Loris Moulin
committed
}
/// The bootnodes addresses the collators will connect to.

Loris Moulin
committed
pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> {

Loris Moulin
committed
self.bootnodes_addresses.iter().collect::<Vec<_>>()
}
/// The collators of the parachain.

Loris Moulin
committed
pub fn collators(&self) -> Vec<&NodeConfig> {
let mut cols = self.collators.iter().collect::<Vec<_>>();
if let Some(col) = self.collator.as_ref() {
cols.push(col);
}
cols
pub mod states {
use crate::shared::macros::states;
states! {
Initial,
WithId,
WithAtLeastOneCollator
}
states! {
Bootstrap,
Running
}
pub trait Context {}
impl Context for Bootstrap {}
impl Context for Running {}

Loris Moulin
committed
}
use states::{Bootstrap, Context, Initial, Running, WithAtLeastOneCollator, WithId};
/// A parachain configuration builder, used to build a [`ParachainConfig`] declaratively with fields validation.
pub struct ParachainConfigBuilder<S, C> {

Loris Moulin
committed
config: ParachainConfig,
validation_context: Rc<RefCell<ValidationContext>>,
errors: Vec<anyhow::Error>,
_state: PhantomData<S>,
_context: PhantomData<C>,

Loris Moulin
committed
}
impl<C: Context> Default for ParachainConfigBuilder<Initial, C> {

Loris Moulin
committed
fn default() -> Self {
Self {

Loris Moulin
committed
config: ParachainConfig {

Loris Moulin
committed
chain: None,
registration_strategy: Some(RegistrationStrategy::InGenesis),

Loris Moulin
committed
onboard_as_parachain: true,

Loris Moulin
committed
initial_balance: 2_000_000_000_000.into(),
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,

Loris Moulin
committed
genesis_state_generator: None,
chain_spec_command: None,
chain_spec_command_is_local: false, // remote by default
is_cumulus_based: true,
bootnodes_addresses: vec![],
collators: vec![],

Loris Moulin
committed
},
validation_context: Default::default(),

Loris Moulin
committed
errors: vec![],

Loris Moulin
committed
_state: PhantomData,

Loris Moulin
committed
}

Loris Moulin
committed
}
impl<A, C> ParachainConfigBuilder<A, C> {
fn transition<B>(
config: ParachainConfig,
validation_context: Rc<RefCell<ValidationContext>>,
errors: Vec<anyhow::Error>,
) -> ParachainConfigBuilder<B, C> {

Loris Moulin
committed
ParachainConfigBuilder {
config,
validation_context,

Loris Moulin
committed
errors,

Loris Moulin
committed
_state: PhantomData,
fn default_chain_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(),

Loris Moulin
committed
}
impl ParachainConfigBuilder<Initial, Bootstrap> {
/// Instantiate a new builder that can be used to build a [`ParachainConfig`] during the bootstrap phase.
pub fn new(
validation_context: Rc<RefCell<ValidationContext>>,
) -> ParachainConfigBuilder<Initial, Bootstrap> {
Self {
validation_context,
..Self::default()
}

Loris Moulin
committed
}

Loris Moulin
committed
impl ParachainConfigBuilder<WithId, Bootstrap> {
/// Set the registration strategy for the parachain, could be Manual (no registered by zombienet) or automatic
/// using an extrinsic or in genesis.
pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self {
Self::transition(
ParachainConfig {
registration_strategy: Some(strategy),
..self.config
},
self.validation_context,
self.errors,
)
}
}
impl ParachainConfigBuilder<WithId, Running> {
/// Set the registration strategy for the parachain, could be Manual (no registered by zombienet) or automatic
/// Using an extrinsic. Genesis option is not allowed in `Running` context.
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self {
match strategy {
RegistrationStrategy::InGenesis => Self::transition(
self.config,
self.validation_context,
merge_errors(
self.errors,
FieldError::RegistrationStrategy(anyhow!(
"Can be set to InGenesis in Running context"
))
.into(),
),
),
RegistrationStrategy::Manual | RegistrationStrategy::UsingExtrinsic => {
Self::transition(
ParachainConfig {
registration_strategy: Some(strategy),
..self.config
},
self.validation_context,
self.errors,
)
},
}
}
}
impl ParachainConfigBuilder<Initial, Running> {
/// Start a new builder in the context of a running network
pub fn new_with_running(
validation_context: Rc<RefCell<ValidationContext>>,
) -> ParachainConfigBuilder<Initial, Running> {
let mut builder = Self {
validation_context,
..Self::default()
};
// override the registration strategy
builder.config.registration_strategy = Some(RegistrationStrategy::UsingExtrinsic);
builder
}
}
impl<C: Context> ParachainConfigBuilder<Initial, C> {
/// Set the parachain ID (should be unique).
// TODO: handle unique validation
pub fn with_id(self, id: u32) -> ParachainConfigBuilder<WithId, C> {
Self::transition(
ParachainConfig { id, ..self.config },
self.validation_context,
self.errors,
)

Loris Moulin
committed
}
}
impl<C: Context> ParachainConfigBuilder<WithId, C> {
/// Set the chain name (e.g. rococo-local).
/// Use [`None`], if you are running adder-collator or undying-collator).

Loris Moulin
committed
pub fn with_chain<T>(self, chain: T) -> Self
where
T: TryInto<Chain>,
T::Error: Error + Send + Sync + 'static,
{
match chain.try_into() {
Ok(chain) => Self::transition(

Loris Moulin
committed
ParachainConfig {

Loris Moulin
committed
chain: Some(chain),

Loris Moulin
committed
..self.config
},
self.validation_context,

Loris Moulin
committed
self.errors,
),
Err(error) => Self::transition(

Loris Moulin
committed
self.config,
self.validation_context,
merge_errors(self.errors, FieldError::Chain(error.into()).into()),

Loris Moulin
committed
),
}

Loris Moulin
committed
}

Loris Moulin
committed
/// Set whether the parachain should be onboarded or stay a parathread. Default is ```true```.
pub fn onboard_as_parachain(self, choice: bool) -> Self {
Self::transition(
ParachainConfig {
onboard_as_parachain: choice,
..self.config
},
self.validation_context,
self.errors,
)
}
/// Set the initial balance of the parachain account.
pub fn with_initial_balance(self, initial_balance: u128) -> Self {
Self::transition(
ParachainConfig {

Loris Moulin
committed
initial_balance: initial_balance.into(),
..self.config
},
self.validation_context,
self.errors,
)
}
/// Set the default command used for collators. Can be overridden.
pub fn with_default_command<T>(self, command: T) -> Self
T: TryInto<Command>,
T::Error: Error + Send + Sync + 'static,
match command.try_into() {
Ok(command) => Self::transition(
default_command: Some(command),
self.validation_context,
Err(error) => Self::transition(
self.validation_context,
merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()),

Loris Moulin
committed
}
/// Set the default container image used for collators. Can be overridden.
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.validation_context,
self.errors,
),
Err(error) => Self::transition(
self.config,
self.validation_context,
merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()),
),
}
}
/// Set the default resources limits used for collators. Can be overridden.
pub fn with_default_resources(
self,
f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder,
) -> Self {
match f(ResourcesBuilder::new()).build() {
Ok(default_resources) => Self::transition(
ParachainConfig {
default_resources: Some(default_resources),
..self.config
},
self.validation_context,
self.errors,
),
Err(errors) => Self::transition(
self.config,
self.validation_context,
merge_errors_vecs(
self.errors,
errors
.into_iter()
.map(|error| FieldError::DefaultResources(error).into())
.collect::<Vec<_>>(),
),
),
}
}
/// Set the default database snapshot location that will be used for state. Can be overridden.
pub fn with_default_db_snapshot(self, location: impl Into<AssetLocation>) -> Self {

Loris Moulin
committed
Self::transition(
ParachainConfig {
default_db_snapshot: Some(location.into()),

Loris Moulin
committed
..self.config
},
self.validation_context,

Loris Moulin
committed
self.errors,
)
/// Set the default arguments that will be used to execute the collator command. Can be overridden.
pub fn with_default_args(self, args: Vec<Arg>) -> Self {

Loris Moulin
committed
Self::transition(
ParachainConfig {
default_args: args,

Loris Moulin
committed
..self.config
},
self.validation_context,

Loris Moulin
committed
self.errors,
)
/// Set the location of a pre-existing genesis WASM runtime blob of the parachain.
pub fn with_genesis_wasm_path(self, location: impl Into<AssetLocation>) -> Self {
Self::transition(
ParachainConfig {
genesis_wasm_path: Some(location.into()),
..self.config
},
self.validation_context,
self.errors,
)
/// Set the generator command used to create the genesis WASM runtime blob of the parachain.
pub fn with_genesis_wasm_generator<T>(self, command: T) -> Self
where
T: TryInto<Command>,
T::Error: Error + Send + Sync + 'static,
match command.try_into() {
Ok(command) => Self::transition(

Loris Moulin
committed
ParachainConfig {
genesis_wasm_generator: Some(command),

Loris Moulin
committed
..self.config
},
self.validation_context,
Err(error) => Self::transition(
self.validation_context,
merge_errors(
self.errors,
FieldError::GenesisWasmGenerator(error.into()).into(),
),

Loris Moulin
committed
),
}
/// Set the location of a pre-existing genesis state of the parachain.
pub fn with_genesis_state_path(self, location: impl Into<AssetLocation>) -> Self {
Self::transition(
ParachainConfig {
genesis_state_path: Some(location.into()),
..self.config
},
self.validation_context,
self.errors,
)
/// Set the generator command used to create the genesis state of the parachain.
pub fn with_genesis_state_generator<T>(self, command: T) -> Self
where
T: TryInto<Command>,
T::Error: Error + Send + Sync + 'static,
match command.try_into() {
Ok(command) => Self::transition(

Loris Moulin
committed
ParachainConfig {
genesis_state_generator: Some(command),

Loris Moulin
committed
..self.config
},
self.validation_context,
Err(error) => Self::transition(
self.validation_context,
merge_errors(
self.errors,
FieldError::GenesisStateGenerator(error.into()).into(),
),

Loris Moulin
committed
),
}
/// Set the genesis overrides as a JSON object.
pub fn with_genesis_overrides(self, genesis_overrides: impl Into<serde_json::Value>) -> Self {
Self::transition(
ParachainConfig {
genesis_overrides: Some(genesis_overrides.into()),
..self.config
},
self.validation_context,
self.errors,
)
}
/// Set the location of a pre-existing chain specification for the parachain.
pub fn with_chain_spec_path(self, location: impl Into<AssetLocation>) -> Self {
Self::transition(
ParachainConfig {
chain_spec_path: Some(location.into()),
..self.config
},
self.validation_context,
self.errors,
)
/// Set the chain-spec command _template_ for the relay chain.
pub fn with_chain_spec_command(self, cmd_template: impl Into<String>) -> Self {
Self::transition(
ParachainConfig {
chain_spec_command: Some(cmd_template.into()),
..self.config
},
self.validation_context,
self.errors,
)
}
/// Set if the chain-spec command needs to be run locally or not (false by default)
pub fn chain_spec_command_is_local(self, choice: bool) -> Self {
Self::transition(
ParachainConfig {
chain_spec_command_is_local: choice,
..self.config
},
self.validation_context,
self.errors,
)
}
/// Set whether the parachain is based on cumulus (true in a majority of case, except adder or undying collators).
pub fn cumulus_based(self, choice: bool) -> Self {

Loris Moulin
committed
Self::transition(
ParachainConfig {
is_cumulus_based: choice,

Loris Moulin
committed
..self.config
},
self.validation_context,

Loris Moulin
committed
self.errors,
)
/// Set the bootnodes addresses the collators will connect to.
pub fn with_bootnodes_addresses<T>(self, bootnodes_addresses: Vec<T>) -> Self
where
T::Error: Error + Send + Sync + 'static,

Loris Moulin
committed
let mut addrs = vec![];
let mut errors = vec![];
for (index, addr) in bootnodes_addresses.into_iter().enumerate() {

Loris Moulin
committed
match addr.try_into() {
Ok(addr) => addrs.push(addr),
Err(error) => errors.push(
FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(),
),

Loris Moulin
committed
}
}
Self::transition(
ParachainConfig {
bootnodes_addresses: addrs,
..self.config
},
self.validation_context,
merge_errors_vecs(self.errors, errors),

Loris Moulin
committed
)
/// Add a new collator using a nested [`NodeConfigBuilder`].
pub fn with_collator(
self,
f: impl FnOnce(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>,
) -> ParachainConfigBuilder<WithAtLeastOneCollator, C> {
match f(NodeConfigBuilder::new(
self.default_chain_context(),
self.validation_context.clone(),
))
.build()
{
Ok(collator) => Self::transition(
ParachainConfig {
collators: vec![collator],
..self.config
},
self.validation_context,
self.errors,
),
Err((name, errors)) => Self::transition(
self.config,
self.validation_context,
merge_errors_vecs(
self.errors,
errors
.into_iter()
.map(|error| ConfigError::Collator(name.clone(), error).into())
.collect::<Vec<_>>(),
),
),
}
}
}
impl<C: Context> ParachainConfigBuilder<WithAtLeastOneCollator, C> {
/// Add a new collator using a nested [`NodeConfigBuilder`].

Loris Moulin
committed
pub fn with_collator(
self,
f: impl FnOnce(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>,

Loris Moulin
committed
) -> Self {
match f(NodeConfigBuilder::new(
ChainDefaultContext::default(),
self.validation_context.clone(),
))
.build()
{

Loris Moulin
committed
Ok(collator) => Self::transition(
ParachainConfig {
collators: [self.config.collators, vec![collator]].concat(),

Loris Moulin
committed
..self.config
},
self.validation_context,

Loris Moulin
committed
self.errors,
),
Err((name, errors)) => Self::transition(
self.config,
self.validation_context,

Loris Moulin
committed
merge_errors_vecs(
self.errors,
errors
.into_iter()
.map(|error| ConfigError::Collator(name.clone(), error).into())
.collect::<Vec<_>>(),
),
),
}

Loris Moulin
committed
}
/// Seals the builder and returns a [`ParachainConfig`] if there are no validation errors, else returns errors.
pub fn build(self) -> Result<ParachainConfig, Vec<anyhow::Error>> {

Loris Moulin
committed
if !self.errors.is_empty() {
return Err(self
.errors
.into_iter()
.map(|error| ConfigError::Parachain(self.config.id, error).into())
.collect::<Vec<_>>());

Loris Moulin
committed
}
Ok(self.config)

Loris Moulin
committed
#[cfg(test)]
mod tests {

Loris Moulin
committed
use super::*;

Loris Moulin
committed
#[test]
fn parachain_config_builder_should_succeeds_and_returns_a_new_parachain_config() {
let parachain_config = ParachainConfigBuilder::new(Default::default())
.with_id(1000)
.with_chain("mychainname")
.with_registration_strategy(RegistrationStrategy::UsingExtrinsic)

Loris Moulin
committed
.onboard_as_parachain(false)
.with_initial_balance(100_000_042)

Loris Moulin
committed
.with_default_image("myrepo:myimage")
.with_default_command("default_command")
.with_default_resources(|resources| {
resources
.with_limit_cpu("500M")
.with_limit_memory("1G")
.with_request_cpu("250M")
})
.with_default_db_snapshot("https://www.urltomysnapshot.com/file.tgz")
.with_default_args(vec![("--arg1", "value1").into(), "--option2".into()])

Loris Moulin
committed
.with_genesis_wasm_path("https://www.backupsite.com/my/wasm/file.tgz")
.with_genesis_wasm_generator("generator_wasm")

Loris Moulin
committed
.with_genesis_state_path("./path/to/genesis/state")
.with_genesis_state_generator("generator_state")

Loris Moulin
committed
.with_chain_spec_path("./path/to/chain/spec.json")
.cumulus_based(false)
.with_bootnodes_addresses(vec
Loris Moulin
committed
"/ip4/10.41.122.55/tcp/45421",
"/ip4/51.144.222.10/tcp/2333",
.with_name("collator1")
.with_command("command1")
.bootnode(true)
})
.with_name("collator2")
.with_command("command2")
.validator(true)
})

Loris Moulin
committed
.build()
.unwrap();
assert_eq!(parachain_config.id(), 1000);
assert_eq!(parachain_config.collators().len(), 2);
let &collator1 = parachain_config.collators().first().unwrap();
assert_eq!(collator1.name(), "collator1");
assert_eq!(collator1.command().unwrap().as_str(), "command1");
assert!(collator1.is_bootnode());
let &collator2 = parachain_config.collators().last().unwrap();
assert_eq!(collator2.name(), "collator2");
assert_eq!(collator2.command().unwrap().as_str(), "command2");

Loris Moulin
committed
assert_eq!(parachain_config.chain().unwrap().as_str(), "mychainname");
assert_eq!(
parachain_config.registration_strategy().unwrap(),
&RegistrationStrategy::UsingExtrinsic
);

Loris Moulin
committed
assert!(!parachain_config.onboard_as_parachain());
assert_eq!(parachain_config.initial_balance(), 100_000_042);

Loris Moulin
committed
assert_eq!(
parachain_config.default_command().unwrap().as_str(),
"default_command"
);
assert_eq!(
parachain_config.default_image().unwrap().as_str(),
"myrepo:myimage"
);
let default_resources = parachain_config.default_resources().unwrap();
assert_eq!(default_resources.limit_cpu().unwrap().as_str(), "500M");
assert_eq!(default_resources.limit_memory().unwrap().as_str(), "1G");
assert_eq!(default_resources.request_cpu().unwrap().as_str(), "250M");
assert!(matches!(
parachain_config.default_db_snapshot().unwrap(),
AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz",
));
assert!(matches!(
parachain_config.chain_spec_path().unwrap(),
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json"
));
let args: Vec<Arg> = vec![("--arg1", "value1").into(), "--option2".into()];
assert_eq!(
parachain_config.default_args(),
args.iter().collect::<Vec<_>>()
);
assert!(matches!(
parachain_config.genesis_wasm_path().unwrap(),

Loris Moulin
committed
AssetLocation::Url(value) if value.as_str() == "https://www.backupsite.com/my/wasm/file.tgz"
parachain_config.genesis_wasm_generator().unwrap().as_str(),
"generator_wasm"
);
assert!(matches!(
parachain_config.genesis_state_path().unwrap(),

Loris Moulin
committed
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/genesis/state"
parachain_config.genesis_state_generator().unwrap().as_str(),
"generator_state"
);
assert!(matches!(
parachain_config.chain_spec_path().unwrap(),

Loris Moulin
committed
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json"
));
assert!(!parachain_config.is_cumulus_based());

Loris Moulin
committed
let bootnodes_addresses: Vec<Multiaddr> = vec
Loris Moulin
committed
assert_eq!(
parachain_config.bootnodes_addresses(),
bootnodes_addresses.iter().collect::<Vec<_>>()
);
fn parachain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() {
let errors = ParachainConfigBuilder::new(Default::default())

Loris Moulin
committed
.with_chain("invalid chain")
.with_collator(|collator| {
collator
.with_name("collator")

Loris Moulin
committed
.with_command("command")
.validator(true)
})
.build()
.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(
errors.first().unwrap().to_string(),
"parachain[1000].chain: 'invalid chain' shouldn't contains whitespace"
);
}
#[test]
fn parachain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() {
let errors = ParachainConfigBuilder::new(Default::default())

Loris Moulin
committed
.with_chain("chain")
.with_default_command("invalid command")
.with_collator(|collator| {
collator

Loris Moulin
committed
.with_name("node")
.with_command("command")
.validator(true)
})
.build()
.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(
errors.first().unwrap().to_string(),
"parachain[1000].default_command: 'invalid command' shouldn't contains whitespace"
);
}
#[test]
fn parachain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() {
let errors = ParachainConfigBuilder::new(Default::default())

Loris Moulin
committed
.with_id(1000)