use crate::errors::Error;
use sc_chain_spec::GenesisConfigBuilderRuntimeCaller;
use std::{
fs::{self, OpenOptions},
io::{self, Write, stdin, stdout},
path::{Path, PathBuf},
};
pub(crate) type HostFunctions = (
sp_statement_store::runtime_api::HostFunctions,
cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
);
pub(crate) fn sanitize(target: &Path) -> Result<(), Error> {
if target.exists() {
print!("\"{}\" directory exists. Do you want to clean it? [y/n]: ", target.display());
stdout().flush()?;
let mut input = String::new();
stdin().read_line(&mut input)?;
if input.trim().to_lowercase() == "y" {
fs::remove_dir_all(target).map_err(|_| Error::Aborted)?;
} else {
return Err(Error::Aborted);
}
}
Ok(())
}
pub fn is_initial_endowment_valid(initial_endowment: &str) -> bool {
initial_endowment.parse::<u128>().is_ok() ||
is_valid_bitwise_left_shift(initial_endowment).is_ok()
}
fn is_valid_bitwise_left_shift(initial_endowment: &str) -> Result<u128, Error> {
let v: Vec<&str> = initial_endowment.split(" << ").collect();
if v.len() < 2 {
return Err(Error::EndowmentError);
}
let left = v[0]
.split('u') .take(1)
.collect::<String>()
.parse::<u128>()
.map_err(|_e| Error::EndowmentError)?;
let right = v[1]
.chars()
.filter(|c| c.is_numeric()) .collect::<String>()
.parse::<u32>()
.map_err(|_e| Error::EndowmentError)?;
left.checked_shl(right).ok_or(Error::EndowmentError)
}
pub(crate) fn write_to_file(path: &Path, contents: &str) -> Result<(), Error> {
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(path)
.map_err(Error::RustfmtError)?;
file.write_all(contents.as_bytes()).map_err(Error::RustfmtError)?;
if path.extension().is_some_and(|ext| ext == "rs") {
let output = std::process::Command::new("rustfmt")
.arg(path.to_str().unwrap())
.output()
.map_err(Error::RustfmtError)?;
if !output.status.success() {
return Err(Error::RustfmtError(io::Error::other(
"rustfmt exited with non-zero status code",
)));
}
}
Ok(())
}
pub fn get_preset_names(binary_path: &PathBuf) -> Result<Vec<String>, Error> {
let binary = fs::read(binary_path)?;
let genesis_config_builder = GenesisConfigBuilderRuntimeCaller::<HostFunctions>::new(&binary);
genesis_config_builder
.preset_names()
.map_err(|e| Error::GenesisBuilderError(e.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ChainTemplate, generator::chain::ChainSpec};
use askama::Template;
use tempfile::tempdir;
#[test]
fn test_write_to_file() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempdir()?;
let chainspec = ChainSpec {
token_symbol: "DOT".to_string(),
decimals: 6,
initial_endowment: "1000000".to_string(),
based_on: ChainTemplate::Standard.to_string(),
};
let file_path = temp_dir.path().join("file.rs");
let _ = fs::write(&file_path, "");
write_to_file(&file_path, chainspec.render().expect("infallible").as_ref())?;
let generated_file_content =
fs::read_to_string(temp_dir.path().join("file.rs")).expect("Failed to read file");
assert!(
generated_file_content
.contains("properties.insert(\"tokenSymbol\".into(), \"DOT\".into());")
);
assert!(
generated_file_content
.contains("properties.insert(\"tokenDecimals\".into(), 6.into());")
);
assert!(generated_file_content.contains("1000000"));
assert!(generated_file_content.contains(
"properties.insert(\"basedOn\".into(), \"r0gue-io/base-parachain\".into());"
));
Ok(())
}
#[test]
fn test_is_initial_endowment_valid() {
assert!(is_initial_endowment_valid("100000"));
assert!(is_initial_endowment_valid("1u64 << 60"));
assert!(!is_initial_endowment_valid("wrong"));
assert!(!is_initial_endowment_valid(" "));
}
#[test]
fn test_left_shift() {
assert_eq!(is_valid_bitwise_left_shift("1 << 60").unwrap(), 1152921504606846976);
let result = is_valid_bitwise_left_shift("wrong");
assert!(result.is_err());
}
#[test]
fn test_get_preset_names() -> Result<(), Box<dyn std::error::Error>> {
let path = PathBuf::from("../../tests/runtimes/base_parachain_benchmark.wasm");
assert!(path.is_file());
let presets = get_preset_names(&path)?;
assert_eq!(presets, vec!["development", "local_testnet"]);
Ok(())
}
}