zombienet_orchestrator/network_spec/
relaychain.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use std::collections::HashMap;

use configuration::{
    shared::{
        resources::Resources,
        types::{Arg, AssetLocation, Chain, Command, Image},
    },
    RelaychainConfig,
};
use serde::Serialize;
use support::replacer::apply_replacements;

use super::node::NodeSpec;
use crate::{
    errors::OrchestratorError,
    generators::chain_spec::{ChainSpec, Context},
    shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
};

/// A relaychain configuration spec
#[derive(Debug, Clone, Serialize)]
pub struct RelaychainSpec {
    /// Chain to use (e.g. rococo-local).
    pub(crate) chain: Chain,

    /// Default command to run the node. Can be overridden on each node.
    pub(crate) default_command: Option<Command>,

    /// Default image to use (only podman/k8s). Can be overridden on each node.
    pub(crate) default_image: Option<Image>,

    /// Default resources. Can be overridden on each node.
    pub(crate) default_resources: Option<Resources>,

    /// Default database snapshot. Can be overridden on each node.
    pub(crate) default_db_snapshot: Option<AssetLocation>,

    /// Default arguments to use in nodes. Can be overridden on each node.
    pub(crate) default_args: Vec<Arg>,

    // chain_spec_path: Option<AssetLocation>,
    pub(crate) chain_spec: ChainSpec,

    /// Set the count of nominators to generator (used with PoS networks).
    pub(crate) random_nominators_count: u32,

    /// Set the max nominators value (used with PoS networks).
    pub(crate) max_nominations: u8,

    /// Genesis overrides as JSON value.
    pub(crate) runtime_genesis_patch: Option<serde_json::Value>,

    /// Wasm override path/url to use.
    pub(crate) wasm_override: Option<AssetLocation>,

    /// Nodes to run.
    pub(crate) nodes: Vec<NodeSpec>,
}

impl RelaychainSpec {
    pub fn from_config(config: &RelaychainConfig) -> Result<RelaychainSpec, OrchestratorError> {
        // Relaychain main command to use, in order:
        // set as `default_command` or
        // use the command of the first node.
        // If non of those is set, return an error.
        let main_cmd = config
            .default_command()
            .or(config.nodes().first().and_then(|node| node.command()))
            .ok_or(OrchestratorError::InvalidConfig(
                "Relaychain, either default_command or first node with a command needs to be set."
                    .to_string(),
            ))?;

        // TODO: internally we use image as String
        let main_image = config
            .default_image()
            .or(config.nodes().first().and_then(|node| node.image()))
            .map(|image| image.as_str().to_string());

        let replacements = HashMap::from([
            ("disableBootnodes", "--disable-default-bootnode"),
            ("mainCommand", main_cmd.as_str()),
        ]);
        let tmpl = if let Some(tmpl) = config.chain_spec_command() {
            apply_replacements(tmpl, &replacements)
        } else {
            apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements)
        };

        let chain_spec = ChainSpec::new(config.chain().as_str(), Context::Relay)
            .set_chain_name(config.chain().as_str())
            .command(tmpl.as_str(), config.chain_spec_command_is_local())
            .image(main_image.clone());

        // Add asset location if present
        let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() {
            chain_spec.asset_location(chain_spec_path.clone())
        } else {
            chain_spec
        };

        // build the `node_specs`
        let chain_context = ChainDefaultContext {
            default_command: config.default_command(),
            default_image: config.default_image(),
            default_resources: config.default_resources(),
            default_db_snapshot: config.default_db_snapshot(),
            default_args: config.default_args(),
        };

        let (nodes, mut errs) = config
            .nodes()
            .iter()
            .map(|node_config| NodeSpec::from_config(node_config, &chain_context))
            .fold((vec![], vec![]), |(mut nodes, mut errs), result| {
                match result {
                    Ok(node) => nodes.push(node),
                    Err(err) => errs.push(err),
                }
                (nodes, errs)
            });

        if !errs.is_empty() {
            // TODO: merge errs, maybe return something like Result<Sometype, Vec<OrchestratorError>>
            return Err(errs.swap_remove(0));
        }

        Ok(RelaychainSpec {
            chain: config.chain().clone(),
            default_command: config.default_command().cloned(),
            default_image: config.default_image().cloned(),
            default_resources: config.default_resources().cloned(),
            default_db_snapshot: config.default_db_snapshot().cloned(),
            wasm_override: config.wasm_override().cloned(),
            default_args: config.default_args().into_iter().cloned().collect(),
            chain_spec,
            random_nominators_count: config.random_nominators_count().unwrap_or(0),
            max_nominations: config.max_nominations().unwrap_or(24),
            runtime_genesis_patch: config.runtime_genesis_patch().cloned(),
            nodes,
        })
    }

    pub fn chain_spec(&self) -> &ChainSpec {
        &self.chain_spec
    }

    pub fn chain_spec_mut(&mut self) -> &mut ChainSpec {
        &mut self.chain_spec
    }
}