379cb741ed
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
1469 lines
50 KiB
Rust
1469 lines
50 KiB
Rust
use std::{cell::RefCell, error::Error, fmt::Display, marker::PhantomData, rc::Rc};
|
|
|
|
use anyhow::anyhow;
|
|
use multiaddr::Multiaddr;
|
|
use serde::{
|
|
de::{self, Visitor},
|
|
ser::SerializeStruct,
|
|
Deserialize, Serialize,
|
|
};
|
|
|
|
use crate::{
|
|
shared::{
|
|
errors::{ConfigError, FieldError},
|
|
helpers::{generate_unique_para_id, merge_errors, merge_errors_vecs},
|
|
node::{self, NodeConfig, NodeConfigBuilder},
|
|
resources::{Resources, ResourcesBuilder},
|
|
types::{
|
|
Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, U128,
|
|
},
|
|
},
|
|
types::CommandWithCustomArgs,
|
|
utils::{default_as_false, default_as_true, default_initial_balance, is_false},
|
|
};
|
|
|
|
/// The registration strategy that will be used for the teyrchain.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum RegistrationStrategy {
|
|
/// The teyrchain will be added to the genesis before spawning.
|
|
InGenesis,
|
|
/// The teyrchain will be registered using an extrinsic after spawning.
|
|
UsingExtrinsic,
|
|
/// The teyrchaing will not be registered and the user can doit after spawning manually.
|
|
Manual,
|
|
}
|
|
|
|
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)?;
|
|
},
|
|
}
|
|
|
|
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>
|
|
where
|
|
A: serde::de::MapAccess<'de>,
|
|
{
|
|
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
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
deserializer.deserialize_struct(
|
|
"RegistrationStrategy",
|
|
&["add_to_genesis", "register_para"],
|
|
RegistrationStrategyVisitor,
|
|
)
|
|
}
|
|
}
|
|
|
|
/// A teyrchain configuration, composed of collators and fine-grained configuration options.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct TeyrchainConfig {
|
|
id: u32,
|
|
#[serde(skip)]
|
|
// unique_id is internally used to allow multiple teyrchains with the same id
|
|
// BUT, only one of them could be register automatically at spawn
|
|
unique_id: String,
|
|
chain: Option<Chain>,
|
|
#[serde(flatten)]
|
|
registration_strategy: Option<RegistrationStrategy>,
|
|
#[serde(
|
|
skip_serializing_if = "super::utils::is_true",
|
|
default = "default_as_true"
|
|
)]
|
|
onboard_as_teyrchain: bool,
|
|
#[serde(rename = "balance", default = "default_initial_balance")]
|
|
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>,
|
|
genesis_wasm_path: Option<AssetLocation>,
|
|
genesis_wasm_generator: Option<Command>,
|
|
genesis_state_path: Option<AssetLocation>,
|
|
genesis_state_generator: Option<CommandWithCustomArgs>,
|
|
chain_spec_path: Option<AssetLocation>,
|
|
// Path or url to override the runtime (:code) in the chain-spec
|
|
wasm_override: 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(rename = "evm_based", default = "default_as_false")]
|
|
is_evm_based: bool,
|
|
#[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
|
|
bootnodes_addresses: Vec<Multiaddr>,
|
|
#[serde(skip_serializing_if = "is_false", default)]
|
|
no_default_bootnodes: bool,
|
|
#[serde(rename = "genesis", skip_serializing_if = "Option::is_none")]
|
|
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 `[teyrchain.collator]` key.
|
|
// NOTE: if the file also contains multiple collators defined in
|
|
// `[[teyrchain.collators]], the single configuration will be added to the bottom.
|
|
pub(crate) collator: Option<NodeConfig>,
|
|
}
|
|
|
|
impl TeyrchainConfig {
|
|
/// The teyrchain ID.
|
|
pub fn id(&self) -> u32 {
|
|
self.id
|
|
}
|
|
|
|
/// The teyrchain unique ID.
|
|
pub fn unique_id(&self) -> &str {
|
|
&self.unique_id
|
|
}
|
|
|
|
/// The chain name.
|
|
pub fn chain(&self) -> Option<&Chain> {
|
|
self.chain.as_ref()
|
|
}
|
|
|
|
/// The registration strategy for the teyrchain.
|
|
pub fn registration_strategy(&self) -> Option<&RegistrationStrategy> {
|
|
self.registration_strategy.as_ref()
|
|
}
|
|
|
|
/// Whether the teyrchain should be onboarded or stay a parathread
|
|
pub fn onboard_as_teyrchain(&self) -> bool {
|
|
self.onboard_as_teyrchain
|
|
}
|
|
|
|
/// Backward compatibility alias for onboard_as_teyrchain() - for external crates using Polkadot SDK terminology.
|
|
pub fn onboard_as_parachain(&self) -> bool {
|
|
self.onboard_as_teyrchain()
|
|
}
|
|
|
|
/// The initial balance of the teyrchain account.
|
|
pub fn initial_balance(&self) -> u128 {
|
|
self.initial_balance.0
|
|
}
|
|
|
|
/// The default command used for collators.
|
|
pub fn default_command(&self) -> Option<&Command> {
|
|
self.default_command.as_ref()
|
|
}
|
|
|
|
/// The default container image used for collators.
|
|
pub fn default_image(&self) -> Option<&Image> {
|
|
self.default_image.as_ref()
|
|
}
|
|
|
|
/// The default resources limits used for collators.
|
|
pub fn default_resources(&self) -> Option<&Resources> {
|
|
self.default_resources.as_ref()
|
|
}
|
|
|
|
/// The default database snapshot location that will be used for state.
|
|
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.
|
|
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 teyrchain.
|
|
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 teyrchain.
|
|
pub fn genesis_wasm_generator(&self) -> Option<&Command> {
|
|
self.genesis_wasm_generator.as_ref()
|
|
}
|
|
|
|
/// The location of a pre-existing genesis state of the teyrchain.
|
|
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 teyrchain.
|
|
pub fn genesis_state_generator(&self) -> Option<&CommandWithCustomArgs> {
|
|
self.genesis_state_generator.as_ref()
|
|
}
|
|
|
|
/// 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 teyrchain.
|
|
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 teyrchain is based on pezcumulus.
|
|
pub fn is_cumulus_based(&self) -> bool {
|
|
self.is_cumulus_based
|
|
}
|
|
|
|
/// Whether the teyrchain is evm based (e.g frontier).
|
|
pub fn is_evm_based(&self) -> bool {
|
|
self.is_evm_based
|
|
}
|
|
|
|
/// The bootnodes addresses the collators will connect to.
|
|
pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> {
|
|
self.bootnodes_addresses.iter().collect::<Vec<_>>()
|
|
}
|
|
|
|
/// Whether to not automatically assign a bootnode role if none of the nodes are marked
|
|
/// as bootnodes.
|
|
pub fn no_default_bootnodes(&self) -> bool {
|
|
self.no_default_bootnodes
|
|
}
|
|
|
|
/// The collators of the teyrchain.
|
|
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
|
|
}
|
|
|
|
/// The location of a wasm runtime to override in the chain-spec.
|
|
pub fn wasm_override(&self) -> Option<&AssetLocation> {
|
|
self.wasm_override.as_ref()
|
|
}
|
|
}
|
|
|
|
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 {}
|
|
}
|
|
|
|
use states::{Bootstrap, Context, Initial, Running, WithAtLeastOneCollator, WithId};
|
|
/// A teyrchain configuration builder, used to build a [`TeyrchainConfig`] declaratively with fields validation.
|
|
pub struct TeyrchainConfigBuilder<S, C> {
|
|
config: TeyrchainConfig,
|
|
validation_context: Rc<RefCell<ValidationContext>>,
|
|
errors: Vec<anyhow::Error>,
|
|
_state: PhantomData<S>,
|
|
_context: PhantomData<C>,
|
|
}
|
|
|
|
impl<C: Context> Default for TeyrchainConfigBuilder<Initial, C> {
|
|
fn default() -> Self {
|
|
Self {
|
|
config: TeyrchainConfig {
|
|
id: 100,
|
|
unique_id: String::from("100"),
|
|
chain: None,
|
|
registration_strategy: Some(RegistrationStrategy::InGenesis),
|
|
onboard_as_teyrchain: true,
|
|
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,
|
|
genesis_state_generator: None,
|
|
genesis_overrides: None,
|
|
chain_spec_path: None,
|
|
chain_spec_command: None,
|
|
wasm_override: None,
|
|
chain_spec_command_is_local: false, // remote by default
|
|
is_cumulus_based: true,
|
|
is_evm_based: false,
|
|
bootnodes_addresses: vec![],
|
|
no_default_bootnodes: false,
|
|
collators: vec![],
|
|
collator: None,
|
|
},
|
|
validation_context: Default::default(),
|
|
errors: vec![],
|
|
_state: PhantomData,
|
|
_context: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<A, C> TeyrchainConfigBuilder<A, C> {
|
|
fn transition<B>(
|
|
config: TeyrchainConfig,
|
|
validation_context: Rc<RefCell<ValidationContext>>,
|
|
errors: Vec<anyhow::Error>,
|
|
) -> TeyrchainConfigBuilder<B, C> {
|
|
TeyrchainConfigBuilder {
|
|
config,
|
|
validation_context,
|
|
errors,
|
|
_state: PhantomData,
|
|
_context: 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(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TeyrchainConfigBuilder<Initial, Bootstrap> {
|
|
/// Instantiate a new builder that can be used to build a [`TeyrchainConfig`] during the bootstrap phase.
|
|
pub fn new(
|
|
validation_context: Rc<RefCell<ValidationContext>>,
|
|
) -> TeyrchainConfigBuilder<Initial, Bootstrap> {
|
|
Self {
|
|
validation_context,
|
|
..Self::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TeyrchainConfigBuilder<WithId, Bootstrap> {
|
|
/// Set the registration strategy for the teyrchain, 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(
|
|
TeyrchainConfig {
|
|
registration_strategy: Some(strategy),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl TeyrchainConfigBuilder<WithId, Running> {
|
|
/// Set the registration strategy for the teyrchain, could be Manual (no registered by zombienet) or automatic
|
|
/// Using an extrinsic. Genesis option is not allowed in `Running` context.
|
|
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(
|
|
TeyrchainConfig {
|
|
registration_strategy: Some(strategy),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TeyrchainConfigBuilder<Initial, Running> {
|
|
/// Start a new builder in the context of a running network
|
|
pub fn new_with_running(
|
|
validation_context: Rc<RefCell<ValidationContext>>,
|
|
) -> TeyrchainConfigBuilder<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> TeyrchainConfigBuilder<Initial, C> {
|
|
/// Set the teyrchain ID and the unique_id (with the suffix `<para_id>-x` if the id is already used)
|
|
pub fn with_id(self, id: u32) -> TeyrchainConfigBuilder<WithId, C> {
|
|
let unique_id = generate_unique_para_id(id, self.validation_context.clone());
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
id,
|
|
unique_id,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<C: Context> TeyrchainConfigBuilder<WithId, C> {
|
|
/// Set the chain name (e.g. rococo-local).
|
|
/// Use [`None`], if you are running adder-collator or undying-collator).
|
|
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(
|
|
TeyrchainConfig {
|
|
chain: Some(chain),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
),
|
|
Err(error) => Self::transition(
|
|
self.config,
|
|
self.validation_context,
|
|
merge_errors(self.errors, FieldError::Chain(error.into()).into()),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Set whether the teyrchain should be onboarded or stay a parathread. Default is ```true```.
|
|
pub fn onboard_as_teyrchain(self, choice: bool) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
onboard_as_teyrchain: choice,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set the initial balance of the teyrchain account.
|
|
pub fn with_initial_balance(self, initial_balance: u128) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
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
|
|
where
|
|
T: TryInto<Command>,
|
|
T::Error: Error + Send + Sync + 'static,
|
|
{
|
|
match command.try_into() {
|
|
Ok(command) => Self::transition(
|
|
TeyrchainConfig {
|
|
default_command: Some(command),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
),
|
|
Err(error) => Self::transition(
|
|
self.config,
|
|
self.validation_context,
|
|
merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// 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(
|
|
TeyrchainConfig {
|
|
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(
|
|
TeyrchainConfig {
|
|
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 {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
default_db_snapshot: Some(location.into()),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
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 {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
default_args: args,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set the location of a pre-existing genesis WASM runtime blob of the teyrchain.
|
|
pub fn with_genesis_wasm_path(self, location: impl Into<AssetLocation>) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
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 teyrchain.
|
|
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(
|
|
TeyrchainConfig {
|
|
genesis_wasm_generator: Some(command),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
),
|
|
Err(error) => Self::transition(
|
|
self.config,
|
|
self.validation_context,
|
|
merge_errors(
|
|
self.errors,
|
|
FieldError::GenesisWasmGenerator(error.into()).into(),
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Set the location of a pre-existing genesis state of the teyrchain.
|
|
pub fn with_genesis_state_path(self, location: impl Into<AssetLocation>) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
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 teyrchain.
|
|
pub fn with_genesis_state_generator<T>(self, command: T) -> Self
|
|
where
|
|
T: TryInto<CommandWithCustomArgs>,
|
|
T::Error: Error + Send + Sync + 'static,
|
|
{
|
|
match command.try_into() {
|
|
Ok(command) => Self::transition(
|
|
TeyrchainConfig {
|
|
genesis_state_generator: Some(command),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
),
|
|
Err(error) => Self::transition(
|
|
self.config,
|
|
self.validation_context,
|
|
merge_errors(
|
|
self.errors,
|
|
FieldError::GenesisStateGenerator(error.into()).into(),
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Set the genesis overrides as a JSON object.
|
|
pub fn with_genesis_overrides(self, genesis_overrides: impl Into<serde_json::Value>) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
genesis_overrides: Some(genesis_overrides.into()),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set the location of a pre-existing chain specification for the teyrchain.
|
|
pub fn with_chain_spec_path(self, location: impl Into<AssetLocation>) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
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(
|
|
TeyrchainConfig {
|
|
chain_spec_command: Some(cmd_template.into()),
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set the location of a wasm to override the chain-spec.
|
|
pub fn with_wasm_override(self, location: impl Into<AssetLocation>) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
wasm_override: Some(location.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(
|
|
TeyrchainConfig {
|
|
chain_spec_command_is_local: choice,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set whether the teyrchain is based on pezcumulus (true in a majority of case, except adder or undying collators).
|
|
pub fn cumulus_based(self, choice: bool) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
is_cumulus_based: choice,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Set whether the teyrchain is evm based (e.g frontier /evm template)
|
|
pub fn evm_based(self, choice: bool) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
is_evm_based: choice,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
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: TryInto<Multiaddr> + Display + Copy,
|
|
T::Error: Error + Send + Sync + 'static,
|
|
{
|
|
let mut addrs = vec![];
|
|
let mut errors = vec![];
|
|
|
|
for (index, addr) in bootnodes_addresses.into_iter().enumerate() {
|
|
match addr.try_into() {
|
|
Ok(addr) => addrs.push(addr),
|
|
Err(error) => errors.push(
|
|
FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(),
|
|
),
|
|
}
|
|
}
|
|
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
bootnodes_addresses: addrs,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
merge_errors_vecs(self.errors, errors),
|
|
)
|
|
}
|
|
|
|
/// Do not assign a bootnode role automatically if no nodes are marked as bootnodes.
|
|
pub fn without_default_bootnodes(self) -> Self {
|
|
Self::transition(
|
|
TeyrchainConfig {
|
|
no_default_bootnodes: true,
|
|
..self.config
|
|
},
|
|
self.validation_context,
|
|
self.errors,
|
|
)
|
|
}
|
|
|
|
/// Add a new collator using a nested [`NodeConfigBuilder`].
|
|
pub fn with_collator(
|
|
self,
|
|
f: impl FnOnce(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>,
|
|
) -> TeyrchainConfigBuilder<WithAtLeastOneCollator, C> {
|
|
match f(NodeConfigBuilder::new(
|
|
self.default_chain_context(),
|
|
self.validation_context.clone(),
|
|
))
|
|
.build()
|
|
{
|
|
Ok(collator) => Self::transition(
|
|
TeyrchainConfig {
|
|
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> TeyrchainConfigBuilder<WithAtLeastOneCollator, C> {
|
|
/// Add a new collator using a nested [`NodeConfigBuilder`].
|
|
pub fn with_collator(
|
|
self,
|
|
f: impl FnOnce(NodeConfigBuilder<node::Initial>) -> NodeConfigBuilder<node::Buildable>,
|
|
) -> Self {
|
|
match f(NodeConfigBuilder::new(
|
|
self.default_chain_context(),
|
|
self.validation_context.clone(),
|
|
))
|
|
.build()
|
|
{
|
|
Ok(collator) => Self::transition(
|
|
TeyrchainConfig {
|
|
collators: [self.config.collators, vec![collator]].concat(),
|
|
..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<_>>(),
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Seals the builder and returns a [`TeyrchainConfig`] if there are no validation errors, else returns errors.
|
|
pub fn build(self) -> Result<TeyrchainConfig, Vec<anyhow::Error>> {
|
|
if !self.errors.is_empty() {
|
|
return Err(self
|
|
.errors
|
|
.into_iter()
|
|
.map(|error| ConfigError::Teyrchain(self.config.id, error).into())
|
|
.collect::<Vec<_>>());
|
|
}
|
|
|
|
Ok(self.config)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::NetworkConfig;
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_succeeds_and_returns_a_new_teyrchain_config() {
|
|
let teyrchain_config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("mychainname")
|
|
.with_registration_strategy(RegistrationStrategy::UsingExtrinsic)
|
|
.onboard_as_teyrchain(false)
|
|
.with_initial_balance(100_000_042)
|
|
.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()])
|
|
.with_genesis_wasm_path("https://www.backupsite.com/my/wasm/file.tgz")
|
|
.with_genesis_wasm_generator("generator_wasm")
|
|
.with_genesis_state_path("./path/to/genesis/state")
|
|
.with_genesis_state_generator(
|
|
"undying-collator export-genesis-state --pov-size=10000 --pvf-complexity=1",
|
|
)
|
|
.with_chain_spec_path("./path/to/chain/spec.json")
|
|
.with_wasm_override("./path/to/override/runtime.wasm")
|
|
.cumulus_based(false)
|
|
.evm_based(false)
|
|
.with_bootnodes_addresses(vec![
|
|
"/ip4/10.41.122.55/tcp/45421",
|
|
"/ip4/51.144.222.10/tcp/2333",
|
|
])
|
|
.without_default_bootnodes()
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator1")
|
|
.with_command("command1")
|
|
.bootnode(true)
|
|
})
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator2")
|
|
.with_command("command2")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert_eq!(teyrchain_config.id(), 1000);
|
|
assert_eq!(teyrchain_config.collators().len(), 2);
|
|
let &collator1 = teyrchain_config.collators().first().unwrap();
|
|
assert_eq!(collator1.name(), "collator1");
|
|
assert_eq!(collator1.command().unwrap().as_str(), "command1");
|
|
assert!(collator1.is_bootnode());
|
|
let &collator2 = teyrchain_config.collators().last().unwrap();
|
|
assert_eq!(collator2.name(), "collator2");
|
|
assert_eq!(collator2.command().unwrap().as_str(), "command2");
|
|
assert!(collator2.is_validator());
|
|
assert_eq!(teyrchain_config.chain().unwrap().as_str(), "mychainname");
|
|
|
|
assert_eq!(
|
|
teyrchain_config.registration_strategy().unwrap(),
|
|
&RegistrationStrategy::UsingExtrinsic
|
|
);
|
|
assert!(!teyrchain_config.onboard_as_teyrchain());
|
|
assert_eq!(teyrchain_config.initial_balance(), 100_000_042);
|
|
assert_eq!(
|
|
teyrchain_config.default_command().unwrap().as_str(),
|
|
"default_command"
|
|
);
|
|
assert_eq!(
|
|
teyrchain_config.default_image().unwrap().as_str(),
|
|
"myrepo:myimage"
|
|
);
|
|
let default_resources = teyrchain_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!(
|
|
teyrchain_config.default_db_snapshot().unwrap(),
|
|
AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz",
|
|
));
|
|
assert!(matches!(
|
|
teyrchain_config.chain_spec_path().unwrap(),
|
|
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json"
|
|
));
|
|
assert!(matches!(
|
|
teyrchain_config.wasm_override().unwrap(),
|
|
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/override/runtime.wasm"
|
|
));
|
|
let args: Vec<Arg> = vec![("--arg1", "value1").into(), "--option2".into()];
|
|
assert_eq!(
|
|
teyrchain_config.default_args(),
|
|
args.iter().collect::<Vec<_>>()
|
|
);
|
|
assert!(matches!(
|
|
teyrchain_config.genesis_wasm_path().unwrap(),
|
|
AssetLocation::Url(value) if value.as_str() == "https://www.backupsite.com/my/wasm/file.tgz"
|
|
));
|
|
assert_eq!(
|
|
teyrchain_config.genesis_wasm_generator().unwrap().as_str(),
|
|
"generator_wasm"
|
|
);
|
|
assert!(matches!(
|
|
teyrchain_config.genesis_state_path().unwrap(),
|
|
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/genesis/state"
|
|
));
|
|
assert_eq!(
|
|
teyrchain_config
|
|
.genesis_state_generator()
|
|
.unwrap()
|
|
.cmd()
|
|
.as_str(),
|
|
"undying-collator"
|
|
);
|
|
|
|
assert_eq!(
|
|
teyrchain_config.genesis_state_generator().unwrap().args(),
|
|
&vec![
|
|
"export-genesis-state".into(),
|
|
("--pov-size", "10000").into(),
|
|
("--pvf-complexity", "1").into()
|
|
]
|
|
);
|
|
|
|
assert!(matches!(
|
|
teyrchain_config.chain_spec_path().unwrap(),
|
|
AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json"
|
|
));
|
|
assert!(!teyrchain_config.is_cumulus_based());
|
|
let bootnodes_addresses: Vec<Multiaddr> = vec![
|
|
"/ip4/10.41.122.55/tcp/45421".try_into().unwrap(),
|
|
"/ip4/51.144.222.10/tcp/2333".try_into().unwrap(),
|
|
];
|
|
assert!(teyrchain_config.no_default_bootnodes());
|
|
assert_eq!(
|
|
teyrchain_config.bootnodes_addresses(),
|
|
bootnodes_addresses.iter().collect::<Vec<_>>()
|
|
);
|
|
assert!(!teyrchain_config.is_evm_based());
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_works_when_genesis_state_generator_contains_args() {
|
|
let teyrchain_config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("myteyrchain")
|
|
.with_genesis_state_generator("generator_state --simple-flag --flag=value")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
teyrchain_config
|
|
.genesis_state_generator()
|
|
.unwrap()
|
|
.cmd()
|
|
.as_str(),
|
|
"generator_state"
|
|
);
|
|
|
|
assert_eq!(
|
|
teyrchain_config
|
|
.genesis_state_generator()
|
|
.unwrap()
|
|
.args()
|
|
.len(),
|
|
2
|
|
);
|
|
|
|
let args = teyrchain_config.genesis_state_generator().unwrap().args();
|
|
|
|
assert_eq!(
|
|
args,
|
|
&vec![
|
|
Arg::Flag("--simple-flag".into()),
|
|
Arg::Option("--flag".into(), "value".into())
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("invalid chain")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[1000].chain: 'invalid chain' shouldn't contains whitespace"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("chain")
|
|
.with_default_command("invalid command")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("node")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[1000].default_command: 'invalid command' shouldn't contains whitespace"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("chain")
|
|
.with_default_image("invalid image")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("node")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
r"teyrchain[1000].default_image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_resources_are_invalid()
|
|
{
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("chain")
|
|
.with_default_resources(|default_resources| {
|
|
default_resources
|
|
.with_limit_memory("100m")
|
|
.with_request_cpu("invalid")
|
|
})
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("node")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
r"teyrchain[1000].default_resources.request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_genesis_wasm_generator_is_invalid(
|
|
) {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_genesis_wasm_generator("invalid command")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator")
|
|
.with_command("command")
|
|
.validator(true)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[2000].genesis_wasm_generator: 'invalid command' shouldn't contains whitespace"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_bootnodes_addresses_are_invalid(
|
|
) {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.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)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 2);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[2000].bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax"
|
|
);
|
|
assert_eq!(
|
|
errors.get(1).unwrap().to_string(),
|
|
"teyrchain[2000].bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: "
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_an_error_if_first_collator_is_invalid() {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(1000)
|
|
.with_chain("myteyrchain")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator")
|
|
.with_command("invalid command")
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[1000].collators['collator'].command: 'invalid command' shouldn't contains whitespace"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_with_at_least_one_collator_should_fails_and_returns_an_error_if_second_collator_is_invalid(
|
|
) {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator1")
|
|
.with_command("command1")
|
|
.invulnerable(true)
|
|
.bootnode(true)
|
|
})
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator2")
|
|
.with_command("invalid command")
|
|
.with_initial_balance(20000000)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[2000].collators['collator2'].command: 'invalid command' shouldn't contains whitespace"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid(
|
|
) {
|
|
let errors = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"])
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator1")
|
|
.with_command("invalid command")
|
|
.invulnerable(true)
|
|
.bootnode(true)
|
|
.with_resources(|resources| {
|
|
resources
|
|
.with_limit_cpu("invalid")
|
|
.with_request_memory("1G")
|
|
})
|
|
})
|
|
.with_collator(|collator| {
|
|
collator
|
|
.with_name("collator2")
|
|
.with_command("command2")
|
|
.with_image("invalid.image")
|
|
.with_initial_balance(20000000)
|
|
})
|
|
.build()
|
|
.unwrap_err();
|
|
|
|
assert_eq!(errors.len(), 5);
|
|
assert_eq!(
|
|
errors.first().unwrap().to_string(),
|
|
"teyrchain[2000].bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax"
|
|
);
|
|
assert_eq!(
|
|
errors.get(1).unwrap().to_string(),
|
|
"teyrchain[2000].bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: "
|
|
);
|
|
assert_eq!(
|
|
errors.get(2).unwrap().to_string(),
|
|
"teyrchain[2000].collators['collator1'].command: 'invalid command' shouldn't contains whitespace"
|
|
);
|
|
assert_eq!(
|
|
errors.get(3).unwrap().to_string(),
|
|
r"teyrchain[2000].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(),
|
|
"teyrchain[2000].collators['collator2'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn import_toml_registration_strategy_should_deserialize() {
|
|
let load_from_toml =
|
|
NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap();
|
|
|
|
for teyrchain in load_from_toml.teyrchains().iter() {
|
|
if teyrchain.id() == 1000 {
|
|
assert_eq!(
|
|
teyrchain.registration_strategy(),
|
|
Some(&RegistrationStrategy::UsingExtrinsic)
|
|
);
|
|
}
|
|
if teyrchain.id() == 2000 {
|
|
assert_eq!(
|
|
teyrchain.registration_strategy(),
|
|
Some(&RegistrationStrategy::InGenesis)
|
|
);
|
|
}
|
|
}
|
|
|
|
let load_from_toml_small = NetworkConfig::load_from_toml(
|
|
"./testing/snapshots/0003-small-network_w_teyrchain.toml",
|
|
)
|
|
.unwrap();
|
|
|
|
let teyrchain = load_from_toml_small.teyrchains()[0];
|
|
let teyrchain_evm = load_from_toml_small.teyrchains()[1];
|
|
|
|
assert_eq!(teyrchain.registration_strategy(), None);
|
|
assert!(!teyrchain.is_evm_based());
|
|
assert_eq!(teyrchain.collators().len(), 1);
|
|
assert!(teyrchain_evm.is_evm_based());
|
|
}
|
|
|
|
#[test]
|
|
fn onboard_as_teyrchain_should_default_to_true() {
|
|
let config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert!(config.onboard_as_teyrchain());
|
|
}
|
|
|
|
#[test]
|
|
fn evm_based_default_to_false() {
|
|
let config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert!(!config.is_evm_based());
|
|
}
|
|
|
|
#[test]
|
|
fn evm_based() {
|
|
let config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.evm_based(true)
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert!(config.is_evm_based());
|
|
}
|
|
|
|
#[test]
|
|
fn build_config_in_running_context() {
|
|
let config = TeyrchainConfigBuilder::new_with_running(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("myteyrchain")
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
config.registration_strategy(),
|
|
Some(&RegistrationStrategy::UsingExtrinsic)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_works_with_chain_spec_command() {
|
|
const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}";
|
|
let config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("some-chain")
|
|
.with_default_image("myrepo:myimage")
|
|
.with_default_command("default_command")
|
|
.with_chain_spec_command(CMD_TPL)
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert_eq!(config.chain_spec_command(), Some(CMD_TPL));
|
|
assert!(!config.chain_spec_command_is_local());
|
|
}
|
|
|
|
#[test]
|
|
fn teyrchain_config_builder_should_works_with_chain_spec_command_and_local() {
|
|
const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}";
|
|
let config = TeyrchainConfigBuilder::new(Default::default())
|
|
.with_id(2000)
|
|
.with_chain("some-chain")
|
|
.with_default_image("myrepo:myimage")
|
|
.with_default_command("default_command")
|
|
.with_chain_spec_command(CMD_TPL)
|
|
.chain_spec_command_is_local(true)
|
|
.with_collator(|collator| collator.with_name("collator"))
|
|
.build()
|
|
.unwrap();
|
|
|
|
assert_eq!(config.chain_spec_command(), Some(CMD_TPL));
|
|
assert!(config.chain_spec_command_is_local());
|
|
}
|
|
}
|