Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deploy tanssi container chains #296

Open
wants to merge 22 commits into
base: al3mart/feat-tanssi-container-chains
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/pop-cli/src/commands/up/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ impl ZombienetCommand {
if Self::source_binaries(&mut zombienet, &cache, self.verbose, self.skip_confirm).await? {
return Ok(());
}
// For container chains, build the specs before spawn the network
// TODO: Is there a better way to identifiy it than to check if there is a hardcoded
// tanssi-node
if zombienet.binaries().any(|b| b.name() == "tanssi-node") {
zombienet.build_container_chain_specs("tanssi-node")?;
}

// Finally spawn network and wait for signal to terminate
let spinner = cliclack::spinner();
Expand Down
44 changes: 26 additions & 18 deletions crates/pop-common/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,37 @@ impl Git {
)?,
};

if let Some(tag_version) = tag_version {
let (object, reference) = repo.revparse_ext(&tag_version).expect("Object not found");
repo.checkout_tree(&object, None).expect("Failed to checkout");
match reference {
// gref is an actual reference like branches or tags
Some(gref) => repo.set_head(gref.name().unwrap()),
// this is a commit, not a reference
None => repo.set_head_detached(object.id()),
}
.expect("Failed to set HEAD");

let git_dir = repo.path();
fs::remove_dir_all(&git_dir)?;
return Ok(Some(tag_version));
match tag_version {
Some(tag) => Self::git_checkout(&repo, tag),
None => {
// Fetch latest release.
let release = Self::fetch_latest_tag(&repo);
if let Some(r) = release {
return Self::git_checkout(&repo, r);
}
// If there are no releases, use main.
let git_dir = repo.path();
fs::remove_dir_all(&git_dir)?;
Ok(release)
},
}
}

// fetch tags from remote
let release = Self::fetch_latest_tag(&repo);
// Checkout to a specific tag or commit from a Git repository.
fn git_checkout(repo: &GitRepository, tag_version: String) -> Result<Option<String>> {
let (object, reference) = repo.revparse_ext(&tag_version).expect("Object not found");
repo.checkout_tree(&object, None).expect("Failed to checkout");
match reference {
// gref is an actual reference like branches or tags
Some(gref) => repo.set_head(gref.name().unwrap()),
// this is a commit, not a reference
None => repo.set_head_detached(object.id()),
}
.expect("Failed to set HEAD");

let git_dir = repo.path();
fs::remove_dir_all(&git_dir)?;
// Or by default the last one
Ok(release)
Ok(Some(tag_version))
}

/// For users that have ssh configuration for cloning repositories.
Expand Down
30 changes: 20 additions & 10 deletions crates/pop-common/src/templates/extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@ pub fn extract_template_files(
target_directory: &Path,
ignore_directories: Option<Vec<String>>,
) -> Result<()> {
let template_directory = repo_directory.join(template_name);
// Recursively copy all directories and files within. Ignores the specified ones.
copy_dir_all(
&template_directory,
target_directory,
&ignore_directories.unwrap_or_else(|| vec![]),
)?;
Ok(())
let template_directory = repo_directory.join(&template_name);
if template_directory.is_dir() {
// Recursively copy all directories and files within. Ignores the specified ones.
copy_dir_all(
&template_directory,
target_directory,
&ignore_directories.unwrap_or_else(|| vec![]),
)?;
return Ok(());
} else {
// If not a dir, just copy the file.
let dst = target_directory.join(&template_name);
// In case the first file being pulled is not a directory,
// Make sure the target directory exists.
fs::create_dir_all(&target_directory)?;
fs::copy(template_directory, &dst)?;
Ok(())
}
}

/// Recursively copy a directory and its files.
Expand All @@ -42,8 +52,8 @@ fn copy_dir_all(
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() &&
ignore_directories.contains(&entry.file_name().to_string_lossy().to_string())
if ty.is_dir()
&& ignore_directories.contains(&entry.file_name().to_string_lossy().to_string())
{
continue;
} else if ty.is_dir() {
Expand Down
131 changes: 129 additions & 2 deletions crates/pop-parachains/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,49 @@ pub fn generate_raw_chain_spec(
Ok(raw_chain_spec)
}

/// Generates a raw chain specification file for container chain.
///
/// # Arguments
/// * `binary_path` - The path to the node binary executable that contains the `build-spec` command.
/// * `chain` - An optional chain to be used. This should be provided if generating a spec for a
/// parachain.
/// * `container_chains` - An optional vector of container chain specification files. These should
/// be provided if generating a spec for a parachain.
/// * `id` - The parachain id for which the chain specification is being generated.
/// * `invulnerables` - An optional vector of invulnerable nodes. These should be provided if
/// generating a spec for a parachain.
/// * `raw_chain_spec` - The path where the generated raw chain specification file will be
/// generated.
pub fn generate_raw_chain_spec_container_chain(
binary_path: &Path,
chain: Option<&str>,
container_chains: Option<Vec<&str>>,
id: &str,
invulnerables: Option<Vec<&str>>,
raw_chain_spec: &Path,
) -> Result<PathBuf, Error> {
check_command_exists(&binary_path, "build-spec")?;
let mut args = vec!["build-spec", "--parachain-id", id];
// Parachain
if let (Some(chain), Some(invulnerables), Some(container_chains)) =
(chain, invulnerables, container_chains)
{
args.extend(&["--chain", chain]);
for container_chain in container_chains {
args.extend(&["--add-container-chain", container_chain]);
}
for invulnerable in invulnerables {
args.extend(&["--invulnerable", invulnerable]);
}
}
// Container Chain
else {
args.extend(["--disable-default-bootnode", "--raw"]);
}
cmd(binary_path, args).stdout_path(raw_chain_spec).stderr_null().run()?;
Ok(raw_chain_spec.to_path_buf())
}

/// Export the WebAssembly runtime for the parachain.
///
/// # Arguments
Expand Down Expand Up @@ -317,8 +360,9 @@ impl ChainSpec {
mod tests {
use super::*;
use crate::{
new_parachain::instantiate_standard_template, templates::Parachain, Config, Error,
Zombienet,
new_parachain::{instantiate_standard_template, instantiate_tanssi_template},
templates::Parachain,
Config, Error, Zombienet,
};
use anyhow::Result;
use pop_common::manifest::Dependency;
Expand Down Expand Up @@ -492,6 +536,89 @@ default_command = "pop-node"
Ok(())
}

#[tokio::test]
async fn generate_raw_chain_spec_container_chain_works() -> Result<()> {
let temp_dir = tempdir().expect("Failed to create temp dir");
instantiate_tanssi_template(&Parachain::TanssiSimple, temp_dir.path(), None)?;
let config = Builder::new().suffix(".toml").tempfile()?;
// Get Zombienet config and fet binary for generate specs
writeln!(
config.as_file(),
"{}",
format!(
r#"
[relaychain]
chain = "paseo-local"

[[parachains]]
id = 1000
chain_spec_path = "{}"
chain = "dancebox-local"
default_command = "tanssi-node"

[[parachains.collators]]
name = "FullNode-1000"

[[parachains.collators]]
name = "Collator1000-01"

[[parachains.collators]]
name = "Collator1000-02"

[[parachains.collators]]
name = "Collator2000-01"

[[parachains.collators]]
name = "Collator2000-02"
"#,
&temp_dir.path().join("tanssi-1000.json").display().to_string()
)
)?;
let mut zombienet = Zombienet::new(
&temp_dir.path(),
config.path().to_str().unwrap(),
None,
None,
None,
None,
None,
)
.await?;
// Fetch the tanssi-node binary
let mut binary_name: String = "".to_string();
for binary in zombienet.binaries().filter(|b| !b.exists() && b.name() == "tanssi-node") {
binary_name = format!("{}-{}", binary.name(), binary.latest().unwrap());
binary.source(true, &(), true).await?;
}

let raw_chain_spec_container_chain = generate_raw_chain_spec_container_chain(
&temp_dir.path().join(binary_name.clone()),
None,
None,
&"2000",
None,
&temp_dir.path().join("raw-container-chain-chainspec.json"),
)?;
assert!(raw_chain_spec_container_chain.exists());

let raw_chain_spec = generate_raw_chain_spec_container_chain(
&temp_dir.path().join(binary_name),
Some("dancebox-local"),
Some(vec![&temp_dir
.path()
.join("raw-container-chain-chainspec.json")
.display()
.to_string()]),
&"1000",
Some(vec!["Collator1000-01", "Collator1000-02", "Collator2000-01", "Collator2000-02"]),
&temp_dir.path().join("raw-parachain-chainspec.json"),
)?;
assert!(raw_chain_spec.exists());
let content = fs::read_to_string(raw_chain_spec.clone()).expect("Could not read file");
assert!(content.contains("\"para_id\": 2000"));
Ok(())
}

#[test]
fn raw_chain_spec_fails_wrong_chain_spec() -> Result<()> {
assert!(matches!(
Expand Down
2 changes: 2 additions & 0 deletions crates/pop-parachains/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum Error {
Aborted,
#[error("Anyhow error: {0}")]
AnyhowError(#[from] anyhow::Error),
#[error("Missing chain {0}")]
ChainNotFound(String),
#[error("{0}")]
CommonError(#[from] pop_common::Error),
#[error("Configuration error: {0}")]
Expand Down
2 changes: 1 addition & 1 deletion crates/pop-parachains/src/new_parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub fn instantiate_standard_template(
/// * `template` - template to generate the container-chain from.
/// * `target` - location where the parachain will be created.
/// * `tag_version` - version to use (`None` to use latest).
fn instantiate_tanssi_template(
pub fn instantiate_tanssi_template(
template: &ContainerChain,
target: &Path,
tag_version: Option<String>,
Expand Down
22 changes: 1 addition & 21 deletions crates/pop-parachains/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,23 +188,6 @@ pub enum Parachain {
)
)]
TanssiSimple,
/// Tanssi EVM enabled container chain template.
#[strum(
serialize = "frontier",
message = "EVM",
detailed_message = "EVM enabled container chain template.",
props(
Provider = "Tanssi",
Repository = "https://github.com/moondance-labs/tanssi",
Network = "./network.toml",
NodeDirectory = "container-chains/nodes",
RuntimeDirectory = "container-chains/runtime-templates",
SupportedVersions = "master",
ExtractableFiles = ".rustfmt.toml,Cargo.toml,Cargo.lock,LICENSE,README.md,rust-toolchain",
)
)]
TanssiFrontier,

// Templates for unit tests below.
#[cfg(test)]
#[strum(
Expand Down Expand Up @@ -303,7 +286,6 @@ mod tests {
("fpt".to_string(), ParityFPT),
// Tanssi.
("simple".to_string(), TanssiSimple),
("frontier".to_string(), TanssiFrontier),
// Test.
("test_01".to_string(), TestTemplate01),
("test_02".to_string(), TestTemplate02),
Expand All @@ -327,7 +309,6 @@ mod tests {
("fpt".to_string(), "https://github.com/paritytech/frontier-parachain-template"),
// Tanssi.
("simple".to_string(), "https://github.com/moondance-labs/tanssi"),
("frontier".to_string(), "https://github.com/moondance-labs/tanssi"),
// Test.
("test_01".to_string(), ""),
("test_02".to_string(), ""),
Expand All @@ -348,7 +329,6 @@ mod tests {
(ParityFPT, Some("./zombienet-config.toml")),
// Tanssi.
(TanssiSimple, Some("./network.toml")),
(TanssiFrontier, Some("./network.toml")),
// Test.
(TestTemplate01, Some("")),
(TestTemplate02, Some("")),
Expand Down Expand Up @@ -392,7 +372,7 @@ mod tests {
assert_eq!(Provider::OpenZeppelin.provides(&template), true);
assert_eq!(Provider::Tanssi.provides(&template), false);
}
if matches!(template, TanssiSimple | TanssiFrontier) {
if matches!(template, TanssiSimple) {
assert_eq!(Provider::Pop.provides(&template), false);
assert_eq!(Provider::Parity.provides(&template), false);
assert_eq!(Provider::OpenZeppelin.provides(&template), false);
Expand Down
Loading