zombienet_orchestrator/generators/
para_artifact.rs

1use std::path::{Path, PathBuf};
2
3use configuration::types::CommandWithCustomArgs;
4use provider::{
5    constants::NODE_CONFIG_DIR,
6    types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile},
7    DynNamespace,
8};
9use serde::{Deserialize, Serialize};
10use support::fs::FileSystem;
11use uuid::Uuid;
12
13use super::errors::GeneratorError;
14use crate::ScopedFilesystem;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub(crate) enum ParaArtifactType {
18    Wasm,
19    State,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub(crate) enum ParaArtifactBuildOption {
24    Path(String),
25    Command(String),
26    CommandWithCustomArgs(CommandWithCustomArgs),
27}
28
29/// Parachain artifact (could be either the genesis state or genesis wasm)
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ParaArtifact {
32    artifact_type: ParaArtifactType,
33    build_option: ParaArtifactBuildOption,
34    artifact_path: Option<PathBuf>,
35    // image to use for building the para artifact
36    image: Option<String>,
37}
38
39impl ParaArtifact {
40    pub(crate) fn new(
41        artifact_type: ParaArtifactType,
42        build_option: ParaArtifactBuildOption,
43    ) -> Self {
44        Self {
45            artifact_type,
46            build_option,
47            artifact_path: None,
48            image: None,
49        }
50    }
51
52    pub(crate) fn image(mut self, image: Option<String>) -> Self {
53        self.image = image;
54        self
55    }
56
57    pub(crate) fn artifact_path(&self) -> Option<&PathBuf> {
58        self.artifact_path.as_ref()
59    }
60
61    pub(crate) async fn build<'a, T>(
62        &mut self,
63        chain_spec_path: Option<impl AsRef<Path>>,
64        artifact_path: impl AsRef<Path>,
65        ns: &DynNamespace,
66        scoped_fs: &ScopedFilesystem<'a, T>,
67        maybe_output_path: Option<PathBuf>,
68    ) -> Result<(), GeneratorError>
69    where
70        T: FileSystem,
71    {
72        let (cmd, custom_args) = match &self.build_option {
73            ParaArtifactBuildOption::Path(path) => {
74                let t = TransferedFile::new(PathBuf::from(path), artifact_path.as_ref().into());
75                scoped_fs.copy_files(vec![&t]).await?;
76                self.artifact_path = Some(artifact_path.as_ref().into());
77                return Ok(()); // work done!
78            },
79            ParaArtifactBuildOption::Command(cmd) => (cmd, &vec![]),
80            ParaArtifactBuildOption::CommandWithCustomArgs(cmd_with_custom_args) => {
81                (
82                    &cmd_with_custom_args.cmd().as_str().to_string(),
83                    cmd_with_custom_args.args(),
84                )
85                // (cmd.cmd_as_str().to_string(), cmd.1)
86            },
87        };
88
89        let generate_subcmd = match self.artifact_type {
90            ParaArtifactType::Wasm => "export-genesis-wasm",
91            ParaArtifactType::State => "export-genesis-state",
92        };
93        // TODO: replace uuid with para_id-random
94        let temp_name = format!("temp-{}-{}", generate_subcmd, Uuid::new_v4());
95
96        // Start with the subcommand, but we'll skip it from custom_args if it's duplicated
97        let mut args: Vec<String> = vec![generate_subcmd.into()];
98
99        let files_to_inject = if let Some(chain_spec_path) = chain_spec_path {
100            // TODO: we should get the full path from the scoped filesystem
101            let chain_spec_path_local = format!(
102                "{}/{}",
103                ns.base_dir().to_string_lossy(),
104                chain_spec_path.as_ref().to_string_lossy()
105            );
106            // Remote path to be injected
107            let chain_spec_path_in_pod = format!(
108                "{}/{}",
109                NODE_CONFIG_DIR,
110                chain_spec_path.as_ref().to_string_lossy()
111            );
112            // Path in the context of the node, this can be different in the context of the providers (e.g native)
113            let chain_spec_path_in_args = if ns.capabilities().prefix_with_full_path {
114                // In native
115                format!(
116                    "{}/{}{}",
117                    ns.base_dir().to_string_lossy(),
118                    &temp_name,
119                    &chain_spec_path_in_pod
120                )
121            } else {
122                chain_spec_path_in_pod.clone()
123            };
124
125            args.push("--chain".into());
126            args.push(chain_spec_path_in_args);
127
128            vec![TransferedFile::new(
129                chain_spec_path_local,
130                chain_spec_path_in_pod,
131            )]
132        } else {
133            vec![]
134        };
135
136        // Add custom args regardless of whether we have a chain spec
137        // Skip the first custom arg if it matches the subcommand (already added above)
138        let custom_args_to_add = if !custom_args.is_empty() {
139            // Check if first arg is the subcommand (as a Flag)
140            let skip_first = matches!(custom_args.first(), Some(configuration::types::Arg::Flag(flag)) if flag == generate_subcmd);
141            if skip_first {
142                &custom_args[1..]
143            } else {
144                custom_args
145            }
146        } else {
147            custom_args
148        };
149
150        for custom_arg in custom_args_to_add {
151            match custom_arg {
152                configuration::types::Arg::Flag(flag) => {
153                    args.push(flag.into());
154                },
155                configuration::types::Arg::Option(flag, flag_value) => {
156                    args.push(flag.into());
157                    args.push(flag_value.into());
158                },
159                configuration::types::Arg::Array(flag, values) => {
160                    args.push(flag.into());
161                    values.iter().for_each(|v| args.push(v.into()));
162                },
163            }
164        }
165
166        let artifact_path_ref = artifact_path.as_ref();
167        let generate_command = GenerateFileCommand::new(cmd.as_str(), artifact_path_ref).args(args);
168        let options = GenerateFilesOptions::with_files(
169            vec![generate_command],
170            self.image.clone(),
171            &files_to_inject,
172            maybe_output_path,
173        )
174        .temp_name(temp_name);
175        ns.generate_files(options).await?;
176        self.artifact_path = Some(artifact_path_ref.into());
177
178        Ok(())
179    }
180}