fix: migrate vendor rustfmt.toml to stable-only features
- Update vendor/pezkuwi-zombienet-sdk/rustfmt.toml to stable-only - Reformat 74 vendor files with stable rustfmt - Remove nightly-only features causing CI failures
This commit is contained in:
+21
-21
@@ -7,25 +7,25 @@ use crate::generators;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OrchestratorError {
|
||||
// TODO: improve invalid config reporting
|
||||
#[error("Invalid network configuration: {0}")]
|
||||
InvalidConfig(String),
|
||||
#[error("Invalid network config to use provider {0}: {1}")]
|
||||
InvalidConfigForProvider(String, String),
|
||||
#[error("Invalid configuration for node: {0}, field: {1}")]
|
||||
InvalidNodeConfig(String, String),
|
||||
#[error("Invariant not fulfilled {0}")]
|
||||
InvariantError(&'static str),
|
||||
#[error("Global network spawn timeout: {0} secs")]
|
||||
GlobalTimeOut(u32),
|
||||
#[error("Generator error: {0}")]
|
||||
GeneratorError(#[from] generators::errors::GeneratorError),
|
||||
#[error("Provider error")]
|
||||
ProviderError(#[from] ProviderError),
|
||||
#[error("FileSystem error")]
|
||||
FileSystemError(#[from] FileSystemError),
|
||||
#[error("Serialization error")]
|
||||
SerializationError(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
SpawnerError(#[from] anyhow::Error),
|
||||
// TODO: improve invalid config reporting
|
||||
#[error("Invalid network configuration: {0}")]
|
||||
InvalidConfig(String),
|
||||
#[error("Invalid network config to use provider {0}: {1}")]
|
||||
InvalidConfigForProvider(String, String),
|
||||
#[error("Invalid configuration for node: {0}, field: {1}")]
|
||||
InvalidNodeConfig(String, String),
|
||||
#[error("Invariant not fulfilled {0}")]
|
||||
InvariantError(&'static str),
|
||||
#[error("Global network spawn timeout: {0} secs")]
|
||||
GlobalTimeOut(u32),
|
||||
#[error("Generator error: {0}")]
|
||||
GeneratorError(#[from] generators::errors::GeneratorError),
|
||||
#[error("Provider error")]
|
||||
ProviderError(#[from] ProviderError),
|
||||
#[error("FileSystem error")]
|
||||
FileSystemError(#[from] FileSystemError),
|
||||
#[error("Serialization error")]
|
||||
SerializationError(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
SpawnerError(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ mod port;
|
||||
|
||||
pub use bootnode_addr::generate as generate_node_bootnode_addr;
|
||||
pub use command::{
|
||||
generate_for_cumulus_node as generate_node_command_cumulus,
|
||||
generate_for_node as generate_node_command, GenCmdOptions,
|
||||
generate_for_cumulus_node as generate_node_command_cumulus,
|
||||
generate_for_node as generate_node_command, GenCmdOptions,
|
||||
};
|
||||
pub use identity::generate as generate_node_identity;
|
||||
pub use key::generate as generate_node_keys;
|
||||
|
||||
+95
-95
@@ -8,21 +8,21 @@ use configuration::types::Arg;
|
||||
/// - `-:insecure-validator` -> removes `--insecure-validator` (normalized)
|
||||
/// - `-:--prometheus-port` -> removes `--prometheus-port`
|
||||
pub fn parse_removal_args(args: &[Arg]) -> Vec<String> {
|
||||
args.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
Arg::Flag(flag) if flag.starts_with("-:") => {
|
||||
let mut flag_to_exclude = flag[2..].to_string();
|
||||
args.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
Arg::Flag(flag) if flag.starts_with("-:") => {
|
||||
let mut flag_to_exclude = flag[2..].to_string();
|
||||
|
||||
// Normalize flag format - ensure it starts with --
|
||||
if !flag_to_exclude.starts_with("--") {
|
||||
flag_to_exclude = format!("--{flag_to_exclude}");
|
||||
}
|
||||
// Normalize flag format - ensure it starts with --
|
||||
if !flag_to_exclude.starts_with("--") {
|
||||
flag_to_exclude = format!("--{flag_to_exclude}");
|
||||
}
|
||||
|
||||
Some(flag_to_exclude)
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
Some(flag_to_exclude)
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Apply arg removals to a vector of string arguments.
|
||||
@@ -35,104 +35,104 @@ pub fn parse_removal_args(args: &[Arg]) -> Vec<String> {
|
||||
/// # Returns
|
||||
/// Filtered vector with specified args removed
|
||||
pub fn apply_arg_removals(args: Vec<String>, removals: &[String]) -> Vec<String> {
|
||||
if removals.is_empty() {
|
||||
return args;
|
||||
}
|
||||
if removals.is_empty() {
|
||||
return args;
|
||||
}
|
||||
|
||||
let mut res = Vec::new();
|
||||
let mut skip_next = false;
|
||||
let mut res = Vec::new();
|
||||
let mut skip_next = false;
|
||||
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if skip_next {
|
||||
skip_next = false;
|
||||
continue;
|
||||
}
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if skip_next {
|
||||
skip_next = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
let should_remove = removals
|
||||
.iter()
|
||||
.any(|removal| arg == removal || arg.starts_with(&format!("{removal}=")));
|
||||
let should_remove = removals
|
||||
.iter()
|
||||
.any(|removal| arg == removal || arg.starts_with(&format!("{removal}=")));
|
||||
|
||||
if should_remove {
|
||||
// Only skip next if this looks like an option (starts with --) and next arg doesn't start with --
|
||||
if !arg.contains("=") && i + 1 < args.len() {
|
||||
let next_arg = &args[i + 1];
|
||||
if !next_arg.starts_with("-") {
|
||||
skip_next = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if should_remove {
|
||||
// Only skip next if this looks like an option (starts with --) and next arg doesn't start with --
|
||||
if !arg.contains("=") && i + 1 < args.len() {
|
||||
let next_arg = &args[i + 1];
|
||||
if !next_arg.starts_with("-") {
|
||||
skip_next = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
res.push(arg.clone());
|
||||
}
|
||||
res.push(arg.clone());
|
||||
}
|
||||
|
||||
res
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_removal_args() {
|
||||
let args = vec![
|
||||
Arg::Flag("-:--insecure-validator-i-know-what-i-do".to_string()),
|
||||
Arg::Flag("--validator".to_string()),
|
||||
Arg::Flag("-:--no-telemetry".to_string()),
|
||||
];
|
||||
#[test]
|
||||
fn test_parse_removal_args() {
|
||||
let args = vec![
|
||||
Arg::Flag("-:--insecure-validator-i-know-what-i-do".to_string()),
|
||||
Arg::Flag("--validator".to_string()),
|
||||
Arg::Flag("-:--no-telemetry".to_string()),
|
||||
];
|
||||
|
||||
let removals = parse_removal_args(&args);
|
||||
assert_eq!(removals.len(), 2);
|
||||
assert!(removals.contains(&"--insecure-validator-i-know-what-i-do".to_string()));
|
||||
assert!(removals.contains(&"--no-telemetry".to_string()));
|
||||
}
|
||||
let removals = parse_removal_args(&args);
|
||||
assert_eq!(removals.len(), 2);
|
||||
assert!(removals.contains(&"--insecure-validator-i-know-what-i-do".to_string()));
|
||||
assert!(removals.contains(&"--no-telemetry".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_arg_removals_flag() {
|
||||
let args = vec![
|
||||
"--validator".to_string(),
|
||||
"--insecure-validator-i-know-what-i-do".to_string(),
|
||||
"--no-telemetry".to_string(),
|
||||
];
|
||||
let removals = vec!["--insecure-validator-i-know-what-i-do".to_string()];
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 2);
|
||||
assert!(res.contains(&"--validator".to_string()));
|
||||
assert!(res.contains(&"--no-telemetry".to_string()));
|
||||
assert!(!res.contains(&"--insecure-validator-i-know-what-i-do".to_string()));
|
||||
}
|
||||
#[test]
|
||||
fn test_apply_arg_removals_flag() {
|
||||
let args = vec![
|
||||
"--validator".to_string(),
|
||||
"--insecure-validator-i-know-what-i-do".to_string(),
|
||||
"--no-telemetry".to_string(),
|
||||
];
|
||||
let removals = vec!["--insecure-validator-i-know-what-i-do".to_string()];
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 2);
|
||||
assert!(res.contains(&"--validator".to_string()));
|
||||
assert!(res.contains(&"--no-telemetry".to_string()));
|
||||
assert!(!res.contains(&"--insecure-validator-i-know-what-i-do".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_arg_removals_option_with_equals() {
|
||||
let args = vec!["--name=alice".to_string(), "--port=30333".to_string()];
|
||||
let removals = vec!["--port".to_string()];
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0], "--name=alice");
|
||||
}
|
||||
#[test]
|
||||
fn test_apply_arg_removals_option_with_equals() {
|
||||
let args = vec!["--name=alice".to_string(), "--port=30333".to_string()];
|
||||
let removals = vec!["--port".to_string()];
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0], "--name=alice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_arg_removals_option_with_space() {
|
||||
let args = vec![
|
||||
"--name".to_string(),
|
||||
"alice".to_string(),
|
||||
"--port".to_string(),
|
||||
"30333".to_string(),
|
||||
];
|
||||
let removals = vec!["--port".to_string()];
|
||||
#[test]
|
||||
fn test_apply_arg_removals_option_with_space() {
|
||||
let args = vec![
|
||||
"--name".to_string(),
|
||||
"alice".to_string(),
|
||||
"--port".to_string(),
|
||||
"30333".to_string(),
|
||||
];
|
||||
let removals = vec!["--port".to_string()];
|
||||
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0], "--name");
|
||||
assert_eq!(res[1], "alice");
|
||||
}
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0], "--name");
|
||||
assert_eq!(res[1], "alice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_arg_removals_empty() {
|
||||
let args = vec!["--validator".to_string()];
|
||||
let removals = vec![];
|
||||
#[test]
|
||||
fn test_apply_arg_removals_empty() {
|
||||
let args = vec!["--validator".to_string()];
|
||||
let removals = vec![];
|
||||
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res, vec!["--validator".to_string()]);
|
||||
}
|
||||
let res = apply_arg_removals(args, &removals);
|
||||
assert_eq!(res, vec!["--validator".to_string()]);
|
||||
}
|
||||
}
|
||||
|
||||
+74
-89
@@ -3,109 +3,94 @@ use std::{fmt::Display, net::IpAddr};
|
||||
use super::errors::GeneratorError;
|
||||
|
||||
pub fn generate<T: AsRef<str> + Display>(
|
||||
peer_id: &str,
|
||||
ip: &IpAddr,
|
||||
port: u16,
|
||||
args: &[T],
|
||||
p2p_cert: &Option<String>,
|
||||
peer_id: &str,
|
||||
ip: &IpAddr,
|
||||
port: u16,
|
||||
args: &[T],
|
||||
p2p_cert: &Option<String>,
|
||||
) -> Result<String, GeneratorError> {
|
||||
let addr = if let Some(index) = args.iter().position(|arg| arg.as_ref().eq("--listen-addr")) {
|
||||
let listen_value = args
|
||||
.as_ref()
|
||||
.get(index + 1)
|
||||
.ok_or(GeneratorError::BootnodeAddrGeneration(
|
||||
"can not generate bootnode address from args".into(),
|
||||
))?
|
||||
.to_string();
|
||||
let addr = if let Some(index) = args.iter().position(|arg| arg.as_ref().eq("--listen-addr")) {
|
||||
let listen_value = args
|
||||
.as_ref()
|
||||
.get(index + 1)
|
||||
.ok_or(GeneratorError::BootnodeAddrGeneration(
|
||||
"can not generate bootnode address from args".into(),
|
||||
))?
|
||||
.to_string();
|
||||
|
||||
let ip_str = ip.to_string();
|
||||
let port_str = port.to_string();
|
||||
let mut parts = listen_value.split('/').collect::<Vec<&str>>();
|
||||
parts[2] = &ip_str;
|
||||
parts[4] = port_str.as_str();
|
||||
parts.join("/")
|
||||
} else {
|
||||
format!("/ip4/{ip}/tcp/{port}/ws")
|
||||
};
|
||||
let ip_str = ip.to_string();
|
||||
let port_str = port.to_string();
|
||||
let mut parts = listen_value.split('/').collect::<Vec<&str>>();
|
||||
parts[2] = &ip_str;
|
||||
parts[4] = port_str.as_str();
|
||||
parts.join("/")
|
||||
} else {
|
||||
format!("/ip4/{ip}/tcp/{port}/ws")
|
||||
};
|
||||
|
||||
let mut addr_with_peer = format!("{addr}/p2p/{peer_id}");
|
||||
if let Some(p2p_cert) = p2p_cert {
|
||||
addr_with_peer.push_str("/certhash/");
|
||||
addr_with_peer.push_str(p2p_cert)
|
||||
}
|
||||
Ok(addr_with_peer)
|
||||
let mut addr_with_peer = format!("{addr}/p2p/{peer_id}");
|
||||
if let Some(p2p_cert) = p2p_cert {
|
||||
addr_with_peer.push_str("/certhash/");
|
||||
addr_with_peer.push_str(p2p_cert)
|
||||
}
|
||||
Ok(addr_with_peer)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use provider::constants::LOCALHOST;
|
||||
use provider::constants::LOCALHOST;
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice_without_args() {
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<&str> = vec![];
|
||||
let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, &args, &None).unwrap();
|
||||
assert_eq!(
|
||||
&bootnode_addr,
|
||||
"/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"
|
||||
);
|
||||
}
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice_without_args() {
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<&str> = vec![];
|
||||
let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, &args, &None).unwrap();
|
||||
assert_eq!(
|
||||
&bootnode_addr,
|
||||
"/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_for_alice_with_listen_addr() {
|
||||
// Should override the ip/port
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<String> = [
|
||||
"--some",
|
||||
"other",
|
||||
"--listen-addr",
|
||||
"/ip4/192.168.100.1/tcp/30333/ws",
|
||||
]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
let bootnode_addr =
|
||||
generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None).unwrap();
|
||||
assert_eq!(
|
||||
&bootnode_addr,
|
||||
"/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn generate_for_alice_with_listen_addr() {
|
||||
// Should override the ip/port
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<String> =
|
||||
["--some", "other", "--listen-addr", "/ip4/192.168.100.1/tcp/30333/ws"]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
let bootnode_addr =
|
||||
generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None).unwrap();
|
||||
assert_eq!(
|
||||
&bootnode_addr,
|
||||
"/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_for_alice_with_listen_addr_without_value_must_fail() {
|
||||
// Should override the ip/port
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<String> = ["--some", "other", "--listen-addr"]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None);
|
||||
#[test]
|
||||
fn generate_for_alice_with_listen_addr_without_value_must_fail() {
|
||||
// Should override the ip/port
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<String> =
|
||||
["--some", "other", "--listen-addr"].iter().map(|x| x.to_string()).collect();
|
||||
let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None);
|
||||
|
||||
assert!(bootnode_addr.is_err());
|
||||
assert!(matches!(
|
||||
bootnode_addr,
|
||||
Err(GeneratorError::BootnodeAddrGeneration(_))
|
||||
));
|
||||
}
|
||||
assert!(bootnode_addr.is_err());
|
||||
assert!(matches!(bootnode_addr, Err(GeneratorError::BootnodeAddrGeneration(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_for_alice_withcert() {
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<&str> = vec![];
|
||||
let bootnode_addr = generate(
|
||||
peer_id,
|
||||
&LOCALHOST,
|
||||
5678,
|
||||
&args,
|
||||
&Some(String::from("data")),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
#[test]
|
||||
fn generate_for_alice_withcert() {
|
||||
let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed
|
||||
let args: Vec<&str> = vec![];
|
||||
let bootnode_addr =
|
||||
generate(peer_id, &LOCALHOST, 5678, &args, &Some(String::from("data"))).unwrap();
|
||||
assert_eq!(
|
||||
&bootnode_addr,
|
||||
"/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm/certhash/data"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1624
-1790
File diff suppressed because it is too large
Load Diff
+448
-536
File diff suppressed because it is too large
Load Diff
+18
-18
@@ -3,22 +3,22 @@ use support::fs::FileSystemError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GeneratorError {
|
||||
#[error("Generating key {0} with input {1}")]
|
||||
KeyGeneration(String, String),
|
||||
#[error("Generating port {0}, err {1}")]
|
||||
PortGeneration(u16, String),
|
||||
#[error("Chain-spec build error: {0}")]
|
||||
ChainSpecGeneration(String),
|
||||
#[error("Provider error: {0}")]
|
||||
ProviderError(#[from] ProviderError),
|
||||
#[error("FileSystem error")]
|
||||
FileSystemError(#[from] FileSystemError),
|
||||
#[error("Generating identity, err {0}")]
|
||||
IdentityGeneration(String),
|
||||
#[error("Generating bootnode address, err {0}")]
|
||||
BootnodeAddrGeneration(String),
|
||||
#[error("Error overriding wasm on raw chain-spec, err {0}")]
|
||||
OverridingWasm(String),
|
||||
#[error("Error overriding raw chain-spec, err {0}")]
|
||||
OverridingRawSpec(String),
|
||||
#[error("Generating key {0} with input {1}")]
|
||||
KeyGeneration(String, String),
|
||||
#[error("Generating port {0}, err {1}")]
|
||||
PortGeneration(u16, String),
|
||||
#[error("Chain-spec build error: {0}")]
|
||||
ChainSpecGeneration(String),
|
||||
#[error("Provider error: {0}")]
|
||||
ProviderError(#[from] ProviderError),
|
||||
#[error("FileSystem error")]
|
||||
FileSystemError(#[from] FileSystemError),
|
||||
#[error("Generating identity, err {0}")]
|
||||
IdentityGeneration(String),
|
||||
#[error("Generating bootnode address, err {0}")]
|
||||
BootnodeAddrGeneration(String),
|
||||
#[error("Error overriding wasm on raw chain-spec, err {0}")]
|
||||
OverridingWasm(String),
|
||||
#[error("Error overriding raw chain-spec, err {0}")]
|
||||
OverridingRawSpec(String),
|
||||
}
|
||||
|
||||
+18
-24
@@ -7,35 +7,29 @@ use super::errors::GeneratorError;
|
||||
// Generate p2p identity for node
|
||||
// return `node-key` and `peerId`
|
||||
pub fn generate(node_name: &str) -> Result<(String, String), GeneratorError> {
|
||||
let key = hex::encode(sha2::Sha256::digest(node_name));
|
||||
let key = hex::encode(sha2::Sha256::digest(node_name));
|
||||
|
||||
let bytes = <[u8; 32]>::from_hex(key.clone()).map_err(|_| {
|
||||
GeneratorError::IdentityGeneration("can not transform hex to [u8;32]".into())
|
||||
})?;
|
||||
let sk = ed25519::SecretKey::try_from_bytes(bytes)
|
||||
.map_err(|_| GeneratorError::IdentityGeneration("can not create sk from bytes".into()))?;
|
||||
let local_identity: Keypair = ed25519::Keypair::from(sk).into();
|
||||
let local_public = local_identity.public();
|
||||
let local_peer_id = local_public.to_peer_id();
|
||||
let bytes = <[u8; 32]>::from_hex(key.clone()).map_err(|_| {
|
||||
GeneratorError::IdentityGeneration("can not transform hex to [u8;32]".into())
|
||||
})?;
|
||||
let sk = ed25519::SecretKey::try_from_bytes(bytes)
|
||||
.map_err(|_| GeneratorError::IdentityGeneration("can not create sk from bytes".into()))?;
|
||||
let local_identity: Keypair = ed25519::Keypair::from(sk).into();
|
||||
let local_public = local_identity.public();
|
||||
let local_peer_id = local_public.to_peer_id();
|
||||
|
||||
Ok((key, local_peer_id.to_base58()))
|
||||
Ok((key, local_peer_id.to_base58()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice() {
|
||||
let s = "alice";
|
||||
let (key, peer_id) = generate(s).unwrap();
|
||||
assert_eq!(
|
||||
&key,
|
||||
"2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90"
|
||||
);
|
||||
assert_eq!(
|
||||
&peer_id,
|
||||
"12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"
|
||||
);
|
||||
}
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice() {
|
||||
let s = "alice";
|
||||
let (key, peer_id) = generate(s).unwrap();
|
||||
assert_eq!(&key, "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90");
|
||||
assert_eq!(&peer_id, "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm");
|
||||
}
|
||||
}
|
||||
|
||||
+111
-121
@@ -1,151 +1,141 @@
|
||||
use pezsp_core::{crypto::SecretStringError, ecdsa, ed25519, keccak_256, sr25519, Pair, H160, H256};
|
||||
use pezsp_core::{
|
||||
crypto::SecretStringError, ecdsa, ed25519, keccak_256, sr25519, Pair, H160, H256,
|
||||
};
|
||||
|
||||
use super::errors::GeneratorError;
|
||||
use crate::shared::types::{Accounts, NodeAccount};
|
||||
const KEYS: [&str; 5] = ["sr", "sr_stash", "ed", "ec", "eth"];
|
||||
|
||||
pub fn generate_pair<T: Pair>(seed: &str) -> Result<T::Pair, SecretStringError> {
|
||||
let pair = T::Pair::from_string(seed, None)?;
|
||||
Ok(pair)
|
||||
let pair = T::Pair::from_string(seed, None)?;
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
pub fn generate(seed: &str) -> Result<Accounts, GeneratorError> {
|
||||
let mut accounts: Accounts = Default::default();
|
||||
for k in KEYS {
|
||||
let (address, public_key) = match k {
|
||||
"sr" => {
|
||||
let pair = generate_pair::<sr25519::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"sr_stash" => {
|
||||
let pair = generate_pair::<sr25519::Pair>(&format!("{seed}//stash"))
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"ed" => {
|
||||
let pair = generate_pair::<ed25519::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"ec" => {
|
||||
let pair = generate_pair::<ecdsa::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"eth" => {
|
||||
let pair = generate_pair::<ecdsa::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
let mut accounts: Accounts = Default::default();
|
||||
for k in KEYS {
|
||||
let (address, public_key) = match k {
|
||||
"sr" => {
|
||||
let pair = generate_pair::<sr25519::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"sr_stash" => {
|
||||
let pair = generate_pair::<sr25519::Pair>(&format!("{seed}//stash"))
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"ed" => {
|
||||
let pair = generate_pair::<ed25519::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"ec" => {
|
||||
let pair = generate_pair::<ecdsa::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
(pair.public().to_string(), hex::encode(pair.public()))
|
||||
},
|
||||
"eth" => {
|
||||
let pair = generate_pair::<ecdsa::Pair>(seed)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?;
|
||||
|
||||
let decompressed = libsecp256k1::PublicKey::parse_compressed(&pair.public().0)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?
|
||||
.serialize();
|
||||
let mut m = [0u8; 64];
|
||||
m.copy_from_slice(&decompressed[1..65]);
|
||||
let account = H160::from(H256::from(keccak_256(&m)));
|
||||
let decompressed = libsecp256k1::PublicKey::parse_compressed(&pair.public().0)
|
||||
.map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?
|
||||
.serialize();
|
||||
let mut m = [0u8; 64];
|
||||
m.copy_from_slice(&decompressed[1..65]);
|
||||
let account = H160::from(H256::from(keccak_256(&m)));
|
||||
|
||||
(hex::encode(account), hex::encode(account))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
accounts.insert(k.into(), NodeAccount::new(address, public_key));
|
||||
}
|
||||
Ok(accounts)
|
||||
(hex::encode(account), hex::encode(account))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
accounts.insert(k.into(), NodeAccount::new(address, public_key));
|
||||
}
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice() {
|
||||
use pezsp_core::crypto::Ss58Codec;
|
||||
let s = "Alice";
|
||||
let seed = format!("//{s}");
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_for_alice() {
|
||||
use pezsp_core::crypto::Ss58Codec;
|
||||
let s = "Alice";
|
||||
let seed = format!("//{s}");
|
||||
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
|
||||
let pair = generate_pair::<ecdsa::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1",
|
||||
format!("0x{}", hex::encode(pair.public()))
|
||||
);
|
||||
let pair = generate_pair::<ecdsa::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1",
|
||||
format!("0x{}", hex::encode(pair.public()))
|
||||
);
|
||||
|
||||
let pair = generate_pair::<ed25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
}
|
||||
let pair = generate_pair::<ed25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_for_zombie() {
|
||||
use pezsp_core::crypto::Ss58Codec;
|
||||
let s = "Zombie";
|
||||
let seed = format!("//{s}");
|
||||
#[test]
|
||||
fn generate_for_zombie() {
|
||||
use pezsp_core::crypto::Ss58Codec;
|
||||
let s = "Zombie";
|
||||
let seed = format!("//{s}");
|
||||
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
}
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed).unwrap();
|
||||
assert_eq!(
|
||||
"5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8",
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_pair_invalid_should_fail() {
|
||||
let s = "Alice";
|
||||
let seed = s.to_string();
|
||||
#[test]
|
||||
fn generate_pair_invalid_should_fail() {
|
||||
let s = "Alice";
|
||||
let seed = s.to_string();
|
||||
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed);
|
||||
assert!(pair.is_err());
|
||||
}
|
||||
let pair = generate_pair::<sr25519::Pair>(&seed);
|
||||
assert!(pair.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_invalid_should_fail() {
|
||||
let s = "Alice";
|
||||
let seed = s.to_string();
|
||||
#[test]
|
||||
fn generate_invalid_should_fail() {
|
||||
let s = "Alice";
|
||||
let seed = s.to_string();
|
||||
|
||||
let pair = generate(&seed);
|
||||
assert!(pair.is_err());
|
||||
assert!(matches!(pair, Err(GeneratorError::KeyGeneration(_, _))));
|
||||
}
|
||||
let pair = generate(&seed);
|
||||
assert!(pair.is_err());
|
||||
assert!(matches!(pair, Err(GeneratorError::KeyGeneration(_, _))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_work() {
|
||||
let s = "Alice";
|
||||
let seed = format!("//{s}");
|
||||
#[test]
|
||||
fn generate_work() {
|
||||
let s = "Alice";
|
||||
let seed = format!("//{s}");
|
||||
|
||||
let pair = generate(&seed).unwrap();
|
||||
let sr = pair.get("sr").unwrap();
|
||||
let sr_stash = pair.get("sr_stash").unwrap();
|
||||
let ed = pair.get("ed").unwrap();
|
||||
let ec = pair.get("ec").unwrap();
|
||||
let eth = pair.get("eth").unwrap();
|
||||
let pair = generate(&seed).unwrap();
|
||||
let sr = pair.get("sr").unwrap();
|
||||
let sr_stash = pair.get("sr_stash").unwrap();
|
||||
let ed = pair.get("ed").unwrap();
|
||||
let ec = pair.get("ec").unwrap();
|
||||
let eth = pair.get("eth").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
sr.address,
|
||||
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
|
||||
);
|
||||
assert_eq!(
|
||||
sr_stash.address,
|
||||
"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY"
|
||||
);
|
||||
assert_eq!(
|
||||
ed.address,
|
||||
"5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("0x{}", ec.public_key),
|
||||
"0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1"
|
||||
);
|
||||
assert_eq!(sr.address, "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
|
||||
assert_eq!(sr_stash.address, "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY");
|
||||
assert_eq!(ed.address, "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu");
|
||||
assert_eq!(
|
||||
format!("0x{}", ec.public_key),
|
||||
"0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
format!("0x{}", eth.public_key),
|
||||
"0xe04cc55ebee1cbce552f250e85c57b70b2e2625b"
|
||||
)
|
||||
}
|
||||
assert_eq!(format!("0x{}", eth.public_key), "0xe04cc55ebee1cbce552f250e85c57b70b2e2625b")
|
||||
}
|
||||
}
|
||||
|
||||
+183
-219
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
vec,
|
||||
path::{Path, PathBuf},
|
||||
vec,
|
||||
};
|
||||
|
||||
use hex::encode;
|
||||
@@ -8,9 +8,9 @@ use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
|
||||
|
||||
use super::errors::GeneratorError;
|
||||
use crate::{
|
||||
generators::keystore_key_types::{parse_keystore_key_types, KeystoreKeyType},
|
||||
shared::types::NodeAccounts,
|
||||
ScopedFilesystem,
|
||||
generators::keystore_key_types::{parse_keystore_key_types, KeystoreKeyType},
|
||||
shared::types::NodeAccounts,
|
||||
ScopedFilesystem,
|
||||
};
|
||||
|
||||
/// Generates keystore files for a node.
|
||||
@@ -25,266 +25,230 @@ use crate::{
|
||||
/// If `keystore_key_types` is empty, all default key types will be generated.
|
||||
/// Otherwise, only the specified key types will be generated.
|
||||
pub async fn generate<'a, T>(
|
||||
acc: &NodeAccounts,
|
||||
node_files_path: impl AsRef<Path>,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
asset_hub_polkadot: bool,
|
||||
keystore_key_types: Vec<&str>,
|
||||
acc: &NodeAccounts,
|
||||
node_files_path: impl AsRef<Path>,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
asset_hub_polkadot: bool,
|
||||
keystore_key_types: Vec<&str>,
|
||||
) -> Result<Vec<PathBuf>, GeneratorError>
|
||||
where
|
||||
T: FileSystem,
|
||||
T: FileSystem,
|
||||
{
|
||||
// Create local keystore
|
||||
scoped_fs.create_dir_all(node_files_path.as_ref()).await?;
|
||||
let mut filenames = vec![];
|
||||
// Create local keystore
|
||||
scoped_fs.create_dir_all(node_files_path.as_ref()).await?;
|
||||
let mut filenames = vec![];
|
||||
|
||||
// Parse the key type specifications
|
||||
let key_types = parse_keystore_key_types(&keystore_key_types, asset_hub_polkadot);
|
||||
// Parse the key type specifications
|
||||
let key_types = parse_keystore_key_types(&keystore_key_types, asset_hub_polkadot);
|
||||
|
||||
let futures: Vec<_> = key_types
|
||||
.iter()
|
||||
.map(|key_type| {
|
||||
let filename = generate_keystore_filename(key_type, acc);
|
||||
let file_path = PathBuf::from(format!(
|
||||
"{}/{}",
|
||||
node_files_path.as_ref().to_string_lossy(),
|
||||
filename
|
||||
));
|
||||
let content = format!("\"{}\"", acc.seed);
|
||||
(filename, scoped_fs.write(file_path, content))
|
||||
})
|
||||
.collect();
|
||||
let futures: Vec<_> = key_types
|
||||
.iter()
|
||||
.map(|key_type| {
|
||||
let filename = generate_keystore_filename(key_type, acc);
|
||||
let file_path = PathBuf::from(format!(
|
||||
"{}/{}",
|
||||
node_files_path.as_ref().to_string_lossy(),
|
||||
filename
|
||||
));
|
||||
let content = format!("\"{}\"", acc.seed);
|
||||
(filename, scoped_fs.write(file_path, content))
|
||||
})
|
||||
.collect();
|
||||
|
||||
for (filename, future) in futures {
|
||||
future.await?;
|
||||
filenames.push(PathBuf::from(filename));
|
||||
}
|
||||
for (filename, future) in futures {
|
||||
future.await?;
|
||||
filenames.push(PathBuf::from(filename));
|
||||
}
|
||||
|
||||
Ok(filenames)
|
||||
Ok(filenames)
|
||||
}
|
||||
|
||||
/// Generates the keystore filename for a given key type.
|
||||
///
|
||||
/// The filename format is: `{hex_encoded_key_type}{public_key}`
|
||||
fn generate_keystore_filename(key_type: &KeystoreKeyType, acc: &NodeAccounts) -> String {
|
||||
let account_key = key_type.scheme.account_key();
|
||||
let pk = acc
|
||||
.accounts
|
||||
.get(account_key)
|
||||
.expect(&format!(
|
||||
"Key '{}' should be set for node {THIS_IS_A_BUG}",
|
||||
account_key
|
||||
))
|
||||
.public_key
|
||||
.as_str();
|
||||
let account_key = key_type.scheme.account_key();
|
||||
let pk = acc
|
||||
.accounts
|
||||
.get(account_key)
|
||||
.expect(&format!("Key '{}' should be set for node {THIS_IS_A_BUG}", account_key))
|
||||
.public_key
|
||||
.as_str();
|
||||
|
||||
format!("{}{}", encode(&key_type.key_type), pk)
|
||||
format!("{}{}", encode(&key_type.key_type), pk)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashMap, ffi::OsString, str::FromStr};
|
||||
use std::{collections::HashMap, ffi::OsString, str::FromStr};
|
||||
|
||||
use support::fs::in_memory::{InMemoryFile, InMemoryFileSystem};
|
||||
use support::fs::in_memory::{InMemoryFile, InMemoryFileSystem};
|
||||
|
||||
use super::*;
|
||||
use crate::shared::types::{NodeAccount, NodeAccounts};
|
||||
use super::*;
|
||||
use crate::shared::types::{NodeAccount, NodeAccounts};
|
||||
|
||||
fn create_test_accounts() -> NodeAccounts {
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert(
|
||||
"sr".to_string(),
|
||||
NodeAccount::new("sr_address", "sr_public_key"),
|
||||
);
|
||||
accounts.insert(
|
||||
"ed".to_string(),
|
||||
NodeAccount::new("ed_address", "ed_public_key"),
|
||||
);
|
||||
accounts.insert(
|
||||
"ec".to_string(),
|
||||
NodeAccount::new("ec_address", "ec_public_key"),
|
||||
);
|
||||
NodeAccounts {
|
||||
seed: "//Alice".to_string(),
|
||||
accounts,
|
||||
}
|
||||
}
|
||||
fn create_test_accounts() -> NodeAccounts {
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert("sr".to_string(), NodeAccount::new("sr_address", "sr_public_key"));
|
||||
accounts.insert("ed".to_string(), NodeAccount::new("ed_address", "ed_public_key"));
|
||||
accounts.insert("ec".to_string(), NodeAccount::new("ec_address", "ec_public_key"));
|
||||
NodeAccounts { seed: "//Alice".to_string(), accounts }
|
||||
}
|
||||
|
||||
fn create_test_fs() -> InMemoryFileSystem {
|
||||
InMemoryFileSystem::new(HashMap::from([(
|
||||
OsString::from_str("/").unwrap(),
|
||||
InMemoryFile::dir(),
|
||||
)]))
|
||||
}
|
||||
fn create_test_fs() -> InMemoryFileSystem {
|
||||
InMemoryFileSystem::new(HashMap::from([(
|
||||
OsString::from_str("/").unwrap(),
|
||||
InMemoryFile::dir(),
|
||||
)]))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn generate_creates_default_keystore_files_when_no_key_types_specified() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let base_dir = "/tmp/test";
|
||||
#[tokio::test]
|
||||
async fn generate_creates_default_keystore_files_when_no_key_types_specified() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let base_dir = "/tmp/test";
|
||||
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir };
|
||||
let key_types: Vec<&str> = vec![];
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir };
|
||||
let key_types: Vec<&str> = vec![];
|
||||
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
assert!(res.is_ok());
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
let filenames = res.unwrap();
|
||||
let filenames = res.unwrap();
|
||||
|
||||
assert!(filenames.len() > 10);
|
||||
assert!(filenames.len() > 10);
|
||||
|
||||
let filename_strs: Vec<String> = filenames
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect();
|
||||
let filename_strs: Vec<String> =
|
||||
filenames.iter().map(|p| p.to_string_lossy().to_string()).collect();
|
||||
|
||||
// Check that aura key is generated (hex of "aura" is 61757261)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("61757261")));
|
||||
// Check that babe key is generated (hex of "babe" is 62616265)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("62616265")));
|
||||
// Check that gran key is generated (hex of "gran" is 6772616e)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("6772616e")));
|
||||
}
|
||||
// Check that aura key is generated (hex of "aura" is 61757261)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("61757261")));
|
||||
// Check that babe key is generated (hex of "babe" is 62616265)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("62616265")));
|
||||
// Check that gran key is generated (hex of "gran" is 6772616e)
|
||||
assert!(filename_strs.iter().any(|f| f.starts_with("6772616e")));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn generate_creates_only_specified_keystore_files() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let base_dir = "/tmp/test";
|
||||
#[tokio::test]
|
||||
async fn generate_creates_only_specified_keystore_files() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let base_dir = "/tmp/test";
|
||||
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir };
|
||||
let key_types = vec!["audi", "gran"];
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir };
|
||||
let key_types = vec!["audi", "gran"];
|
||||
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
|
||||
assert!(res.is_ok());
|
||||
assert!(res.is_ok());
|
||||
|
||||
let filenames = res.unwrap();
|
||||
assert_eq!(filenames.len(), 2);
|
||||
let filenames = res.unwrap();
|
||||
assert_eq!(filenames.len(), 2);
|
||||
|
||||
let filename_strs: Vec<String> = filenames
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect();
|
||||
let filename_strs: Vec<String> =
|
||||
filenames.iter().map(|p| p.to_string_lossy().to_string()).collect();
|
||||
|
||||
// audi uses sr scheme by default
|
||||
assert!(filename_strs
|
||||
.iter()
|
||||
.any(|f| f.starts_with("61756469") && f.contains("sr_public_key")));
|
||||
// gran uses ed scheme by default
|
||||
assert!(filename_strs
|
||||
.iter()
|
||||
.any(|f| f.starts_with("6772616e") && f.contains("ed_public_key")));
|
||||
}
|
||||
// audi uses sr scheme by default
|
||||
assert!(filename_strs
|
||||
.iter()
|
||||
.any(|f| f.starts_with("61756469") && f.contains("sr_public_key")));
|
||||
// gran uses ed scheme by default
|
||||
assert!(filename_strs
|
||||
.iter()
|
||||
.any(|f| f.starts_with("6772616e") && f.contains("ed_public_key")));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn generate_produces_correct_keystore_files() {
|
||||
struct TestCase {
|
||||
name: &'static str,
|
||||
key_types: Vec<&'static str>,
|
||||
asset_hub_polkadot: bool,
|
||||
expected_prefix: &'static str,
|
||||
expected_public_key: &'static str,
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn generate_produces_correct_keystore_files() {
|
||||
struct TestCase {
|
||||
name: &'static str,
|
||||
key_types: Vec<&'static str>,
|
||||
asset_hub_polkadot: bool,
|
||||
expected_prefix: &'static str,
|
||||
expected_public_key: &'static str,
|
||||
}
|
||||
|
||||
let test_cases = vec![
|
||||
TestCase {
|
||||
name: "explicit scheme override (gran_sr)",
|
||||
key_types: vec!["gran_sr"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "6772616e", // "gran" in hex
|
||||
expected_public_key: "sr_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "aura with asset_hub_polkadot uses ed",
|
||||
key_types: vec!["aura"],
|
||||
asset_hub_polkadot: true,
|
||||
expected_prefix: "61757261", // "aura" in hex
|
||||
expected_public_key: "ed_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "aura without asset_hub_polkadot uses sr",
|
||||
key_types: vec!["aura"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "61757261", // "aura" in hex
|
||||
expected_public_key: "sr_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "custom key type with explicit ec scheme",
|
||||
key_types: vec!["cust_ec"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "63757374", // "cust" in hex
|
||||
expected_public_key: "ec_public_key",
|
||||
},
|
||||
];
|
||||
let test_cases = vec![
|
||||
TestCase {
|
||||
name: "explicit scheme override (gran_sr)",
|
||||
key_types: vec!["gran_sr"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "6772616e", // "gran" in hex
|
||||
expected_public_key: "sr_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "aura with asset_hub_polkadot uses ed",
|
||||
key_types: vec!["aura"],
|
||||
asset_hub_polkadot: true,
|
||||
expected_prefix: "61757261", // "aura" in hex
|
||||
expected_public_key: "ed_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "aura without asset_hub_polkadot uses sr",
|
||||
key_types: vec!["aura"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "61757261", // "aura" in hex
|
||||
expected_public_key: "sr_public_key",
|
||||
},
|
||||
TestCase {
|
||||
name: "custom key type with explicit ec scheme",
|
||||
key_types: vec!["cust_ec"],
|
||||
asset_hub_polkadot: false,
|
||||
expected_prefix: "63757374", // "cust" in hex
|
||||
expected_public_key: "ec_public_key",
|
||||
},
|
||||
];
|
||||
|
||||
for tc in test_cases {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let scoped_fs = ScopedFilesystem {
|
||||
fs: &fs,
|
||||
base_dir: "/tmp/test",
|
||||
};
|
||||
for tc in test_cases {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/test" };
|
||||
|
||||
let key_types: Vec<&str> = tc.key_types.clone();
|
||||
let res = generate(
|
||||
&accounts,
|
||||
"node1",
|
||||
&scoped_fs,
|
||||
tc.asset_hub_polkadot,
|
||||
key_types,
|
||||
)
|
||||
.await;
|
||||
let key_types: Vec<&str> = tc.key_types.clone();
|
||||
let res =
|
||||
generate(&accounts, "node1", &scoped_fs, tc.asset_hub_polkadot, key_types).await;
|
||||
|
||||
assert!(
|
||||
res.is_ok(),
|
||||
"[{}] Expected Ok but got: {:?}",
|
||||
tc.name,
|
||||
res.err()
|
||||
);
|
||||
let filenames = res.unwrap();
|
||||
assert!(res.is_ok(), "[{}] Expected Ok but got: {:?}", tc.name, res.err());
|
||||
let filenames = res.unwrap();
|
||||
|
||||
assert_eq!(filenames.len(), 1, "[{}] Expected 1 file", tc.name);
|
||||
assert_eq!(filenames.len(), 1, "[{}] Expected 1 file", tc.name);
|
||||
|
||||
let filename = filenames[0].to_string_lossy().to_string();
|
||||
assert!(
|
||||
filename.starts_with(tc.expected_prefix),
|
||||
"[{}] Expected prefix '{}', got '{}'",
|
||||
tc.name,
|
||||
tc.expected_prefix,
|
||||
filename
|
||||
);
|
||||
assert!(
|
||||
filename.contains(tc.expected_public_key),
|
||||
"[{}] Expected public key '{}' in '{}'",
|
||||
tc.name,
|
||||
tc.expected_public_key,
|
||||
filename
|
||||
);
|
||||
}
|
||||
}
|
||||
let filename = filenames[0].to_string_lossy().to_string();
|
||||
assert!(
|
||||
filename.starts_with(tc.expected_prefix),
|
||||
"[{}] Expected prefix '{}', got '{}'",
|
||||
tc.name,
|
||||
tc.expected_prefix,
|
||||
filename
|
||||
);
|
||||
assert!(
|
||||
filename.contains(tc.expected_public_key),
|
||||
"[{}] Expected public key '{}' in '{}'",
|
||||
tc.name,
|
||||
tc.expected_public_key,
|
||||
filename
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn generate_ignores_invalid_key_specs_and_uses_defaults() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let scoped_fs = ScopedFilesystem {
|
||||
fs: &fs,
|
||||
base_dir: "/tmp/test",
|
||||
};
|
||||
#[tokio::test]
|
||||
async fn generate_ignores_invalid_key_specs_and_uses_defaults() {
|
||||
let accounts = create_test_accounts();
|
||||
let fs = create_test_fs();
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/test" };
|
||||
|
||||
let key_types = vec![
|
||||
"invalid", // Too long
|
||||
"xxx", // Too short
|
||||
"audi_xx", // Invalid sceme
|
||||
];
|
||||
let key_types = vec![
|
||||
"invalid", // Too long
|
||||
"xxx", // Too short
|
||||
"audi_xx", // Invalid sceme
|
||||
];
|
||||
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await;
|
||||
|
||||
assert!(res.is_ok());
|
||||
let filenames = res.unwrap();
|
||||
assert!(res.is_ok());
|
||||
let filenames = res.unwrap();
|
||||
|
||||
// Should fall back to defaults since all specs are invalid
|
||||
assert!(filenames.len() > 10);
|
||||
}
|
||||
// Should fall back to defaults since all specs are invalid
|
||||
assert!(filenames.len() > 10);
|
||||
}
|
||||
}
|
||||
|
||||
+188
-193
@@ -5,94 +5,91 @@ use serde::{Deserialize, Serialize};
|
||||
/// Supported cryptographic schemes for keystore keys.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum KeyScheme {
|
||||
/// Sr25519 scheme
|
||||
Sr,
|
||||
/// Ed25519 scheme
|
||||
Ed,
|
||||
/// ECDSA scheme
|
||||
Ec,
|
||||
/// Sr25519 scheme
|
||||
Sr,
|
||||
/// Ed25519 scheme
|
||||
Ed,
|
||||
/// ECDSA scheme
|
||||
Ec,
|
||||
}
|
||||
|
||||
impl KeyScheme {
|
||||
/// Returns the account key suffix used in `NodeAccounts` for this scheme.
|
||||
pub fn account_key(&self) -> &'static str {
|
||||
match self {
|
||||
KeyScheme::Sr => "sr",
|
||||
KeyScheme::Ed => "ed",
|
||||
KeyScheme::Ec => "ec",
|
||||
}
|
||||
}
|
||||
/// Returns the account key suffix used in `NodeAccounts` for this scheme.
|
||||
pub fn account_key(&self) -> &'static str {
|
||||
match self {
|
||||
KeyScheme::Sr => "sr",
|
||||
KeyScheme::Ed => "ed",
|
||||
KeyScheme::Ec => "ec",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for KeyScheme {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
KeyScheme::Sr => write!(f, "sr"),
|
||||
KeyScheme::Ed => write!(f, "ed"),
|
||||
KeyScheme::Ec => write!(f, "ec"),
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
KeyScheme::Sr => write!(f, "sr"),
|
||||
KeyScheme::Ed => write!(f, "ed"),
|
||||
KeyScheme::Ec => write!(f, "ec"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for KeyScheme {
|
||||
type Error = String;
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value.to_lowercase().as_str() {
|
||||
"sr" => Ok(KeyScheme::Sr),
|
||||
"ed" => Ok(KeyScheme::Ed),
|
||||
"ec" => Ok(KeyScheme::Ec),
|
||||
_ => Err(format!("Unsupported key scheme: {}", value)),
|
||||
}
|
||||
}
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value.to_lowercase().as_str() {
|
||||
"sr" => Ok(KeyScheme::Sr),
|
||||
"ed" => Ok(KeyScheme::Ed),
|
||||
"ec" => Ok(KeyScheme::Ec),
|
||||
_ => Err(format!("Unsupported key scheme: {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed keystore key type.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct KeystoreKeyType {
|
||||
/// The 4-character key type identifier (e.g., "aura", "babe", "gran").
|
||||
pub key_type: String,
|
||||
/// The cryptographic scheme to use for this key type.
|
||||
pub scheme: KeyScheme,
|
||||
/// The 4-character key type identifier (e.g., "aura", "babe", "gran").
|
||||
pub key_type: String,
|
||||
/// The cryptographic scheme to use for this key type.
|
||||
pub scheme: KeyScheme,
|
||||
}
|
||||
|
||||
impl KeystoreKeyType {
|
||||
pub fn new(key_type: impl Into<String>, scheme: KeyScheme) -> Self {
|
||||
Self {
|
||||
key_type: key_type.into(),
|
||||
scheme,
|
||||
}
|
||||
}
|
||||
pub fn new(key_type: impl Into<String>, scheme: KeyScheme) -> Self {
|
||||
Self { key_type: key_type.into(), scheme }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default predefined key schemes for known key types.
|
||||
/// Special handling for `aura` when `is_asset_hub_polkadot` is true.
|
||||
fn get_predefined_schemes(is_asset_hub_polkadot: bool) -> HashMap<&'static str, KeyScheme> {
|
||||
let mut schemes = HashMap::new();
|
||||
let mut schemes = HashMap::new();
|
||||
|
||||
// aura has special handling for asset-hub-polkadot
|
||||
if is_asset_hub_polkadot {
|
||||
schemes.insert("aura", KeyScheme::Ed);
|
||||
} else {
|
||||
schemes.insert("aura", KeyScheme::Sr);
|
||||
}
|
||||
// aura has special handling for asset-hub-polkadot
|
||||
if is_asset_hub_polkadot {
|
||||
schemes.insert("aura", KeyScheme::Ed);
|
||||
} else {
|
||||
schemes.insert("aura", KeyScheme::Sr);
|
||||
}
|
||||
|
||||
schemes.insert("babe", KeyScheme::Sr);
|
||||
schemes.insert("imon", KeyScheme::Sr);
|
||||
schemes.insert("gran", KeyScheme::Ed);
|
||||
schemes.insert("audi", KeyScheme::Sr);
|
||||
schemes.insert("asgn", KeyScheme::Sr);
|
||||
schemes.insert("para", KeyScheme::Sr);
|
||||
schemes.insert("beef", KeyScheme::Ec);
|
||||
schemes.insert("nmbs", KeyScheme::Sr); // Nimbus
|
||||
schemes.insert("rand", KeyScheme::Sr); // Randomness (Moonbeam)
|
||||
schemes.insert("rate", KeyScheme::Ed); // Equilibrium rate module
|
||||
schemes.insert("acco", KeyScheme::Sr);
|
||||
schemes.insert("bcsv", KeyScheme::Sr); // BlockchainSrvc (StorageHub)
|
||||
schemes.insert("ftsv", KeyScheme::Ed); // FileTransferSrvc (StorageHub)
|
||||
schemes.insert("mixn", KeyScheme::Sr); // Mixnet
|
||||
schemes.insert("babe", KeyScheme::Sr);
|
||||
schemes.insert("imon", KeyScheme::Sr);
|
||||
schemes.insert("gran", KeyScheme::Ed);
|
||||
schemes.insert("audi", KeyScheme::Sr);
|
||||
schemes.insert("asgn", KeyScheme::Sr);
|
||||
schemes.insert("para", KeyScheme::Sr);
|
||||
schemes.insert("beef", KeyScheme::Ec);
|
||||
schemes.insert("nmbs", KeyScheme::Sr); // Nimbus
|
||||
schemes.insert("rand", KeyScheme::Sr); // Randomness (Moonbeam)
|
||||
schemes.insert("rate", KeyScheme::Ed); // Equilibrium rate module
|
||||
schemes.insert("acco", KeyScheme::Sr);
|
||||
schemes.insert("bcsv", KeyScheme::Sr); // BlockchainSrvc (StorageHub)
|
||||
schemes.insert("ftsv", KeyScheme::Ed); // FileTransferSrvc (StorageHub)
|
||||
schemes.insert("mixn", KeyScheme::Sr); // Mixnet
|
||||
|
||||
schemes
|
||||
schemes
|
||||
}
|
||||
|
||||
/// Parses a single keystore key type specification string.
|
||||
@@ -103,26 +100,26 @@ fn get_predefined_schemes(is_asset_hub_polkadot: bool) -> HashMap<&'static str,
|
||||
///
|
||||
/// Returns `None` if the spec is invalid or doesn't match the expected format.
|
||||
fn parse_key_spec(spec: &str, predefined: &HashMap<&str, KeyScheme>) -> Option<KeystoreKeyType> {
|
||||
let spec = spec.trim();
|
||||
let spec = spec.trim();
|
||||
|
||||
// Try parsing as long form first: key_type_scheme (e.g., "audi_sr")
|
||||
if let Some((key_type, scheme_str)) = spec.split_once('_') {
|
||||
if key_type.len() != 4 {
|
||||
return None;
|
||||
}
|
||||
// Try parsing as long form first: key_type_scheme (e.g., "audi_sr")
|
||||
if let Some((key_type, scheme_str)) = spec.split_once('_') {
|
||||
if key_type.len() != 4 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let scheme = KeyScheme::try_from(scheme_str).ok()?;
|
||||
return Some(KeystoreKeyType::new(key_type, scheme));
|
||||
}
|
||||
let scheme = KeyScheme::try_from(scheme_str).ok()?;
|
||||
return Some(KeystoreKeyType::new(key_type, scheme));
|
||||
}
|
||||
|
||||
// Try parsing as short form: key_type only (e.g., "audi")
|
||||
if spec.len() == 4 {
|
||||
// Look up predefined scheme; default to Sr if not found
|
||||
let scheme = predefined.get(spec).copied().unwrap_or(KeyScheme::Sr);
|
||||
return Some(KeystoreKeyType::new(spec, scheme));
|
||||
}
|
||||
// Try parsing as short form: key_type only (e.g., "audi")
|
||||
if spec.len() == 4 {
|
||||
// Look up predefined scheme; default to Sr if not found
|
||||
let scheme = predefined.get(spec).copied().unwrap_or(KeyScheme::Sr);
|
||||
return Some(KeystoreKeyType::new(spec, scheme));
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
}
|
||||
|
||||
/// Parses a list of keystore key type specifications.
|
||||
@@ -132,151 +129,149 @@ fn parse_key_spec(spec: &str, predefined: &HashMap<&str, KeyScheme>) -> Option<K
|
||||
///
|
||||
/// If the resulting list is empty, returns the default keystore key types.
|
||||
pub fn parse_keystore_key_types<T: AsRef<str>>(
|
||||
specs: &[T],
|
||||
is_asset_hub_polkadot: bool,
|
||||
specs: &[T],
|
||||
is_asset_hub_polkadot: bool,
|
||||
) -> Vec<KeystoreKeyType> {
|
||||
let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot);
|
||||
let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot);
|
||||
|
||||
let parsed: Vec<KeystoreKeyType> = specs
|
||||
.iter()
|
||||
.filter_map(|spec| parse_key_spec(spec.as_ref(), &predefined_schemes))
|
||||
.collect();
|
||||
let parsed: Vec<KeystoreKeyType> = specs
|
||||
.iter()
|
||||
.filter_map(|spec| parse_key_spec(spec.as_ref(), &predefined_schemes))
|
||||
.collect();
|
||||
|
||||
if parsed.is_empty() {
|
||||
get_default_keystore_key_types(is_asset_hub_polkadot)
|
||||
} else {
|
||||
parsed
|
||||
}
|
||||
if parsed.is_empty() {
|
||||
get_default_keystore_key_types(is_asset_hub_polkadot)
|
||||
} else {
|
||||
parsed
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default keystore key types when none are specified.
|
||||
pub fn get_default_keystore_key_types(is_asset_hub_polkadot: bool) -> Vec<KeystoreKeyType> {
|
||||
let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot);
|
||||
let default_keys = [
|
||||
"aura", "babe", "imon", "gran", "audi", "asgn", "para", "beef", "nmbs", "rand", "rate",
|
||||
"mixn", "bcsv", "ftsv",
|
||||
];
|
||||
let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot);
|
||||
let default_keys = [
|
||||
"aura", "babe", "imon", "gran", "audi", "asgn", "para", "beef", "nmbs", "rand", "rate",
|
||||
"mixn", "bcsv", "ftsv",
|
||||
];
|
||||
|
||||
default_keys
|
||||
.iter()
|
||||
.filter_map(|key_type| {
|
||||
predefined_schemes
|
||||
.get(*key_type)
|
||||
.map(|scheme| KeystoreKeyType::new(*key_type, *scheme))
|
||||
})
|
||||
.collect()
|
||||
default_keys
|
||||
.iter()
|
||||
.filter_map(|key_type| {
|
||||
predefined_schemes.get(*key_type).map(|scheme| KeystoreKeyType::new(*key_type, *scheme))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_keystore_key_types_ignores_invalid_specs() {
|
||||
let specs = vec![
|
||||
"audi".to_string(),
|
||||
"invalid".to_string(), // Too long - ignored
|
||||
"xxx".to_string(), // Too short - ignored
|
||||
"xxxx".to_string(), // Unknown key - defaults to sr
|
||||
"audi_xx".to_string(), // Invalid scheme - ignored
|
||||
"gran".to_string(),
|
||||
];
|
||||
#[test]
|
||||
fn parse_keystore_key_types_ignores_invalid_specs() {
|
||||
let specs = vec![
|
||||
"audi".to_string(),
|
||||
"invalid".to_string(), // Too long - ignored
|
||||
"xxx".to_string(), // Too short - ignored
|
||||
"xxxx".to_string(), // Unknown key - defaults to sr
|
||||
"audi_xx".to_string(), // Invalid scheme - ignored
|
||||
"gran".to_string(),
|
||||
];
|
||||
|
||||
let result = parse_keystore_key_types(&specs, false);
|
||||
assert_eq!(result.len(), 3);
|
||||
assert_eq!(result[1], KeystoreKeyType::new("xxxx", KeyScheme::Sr)); // Unknown defaults to sr
|
||||
assert_eq!(result[2], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
}
|
||||
let result = parse_keystore_key_types(&specs, false);
|
||||
assert_eq!(result.len(), 3);
|
||||
assert_eq!(result[1], KeystoreKeyType::new("xxxx", KeyScheme::Sr)); // Unknown defaults to sr
|
||||
assert_eq!(result[2], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_keystore_key_types_returns_specified_keys() {
|
||||
let specs = vec!["audi".to_string(), "gran".to_string()];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
#[test]
|
||||
fn parse_keystore_key_types_returns_specified_keys() {
|
||||
let specs = vec!["audi".to_string(), "gran".to_string()];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
}
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_keystore_key_types_mixed_short_and_long_forms() {
|
||||
let specs = vec![
|
||||
"audi".to_string(),
|
||||
"gran_sr".to_string(), // Override gran's default ed to sr
|
||||
"gran".to_string(),
|
||||
"beef".to_string(),
|
||||
];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
#[test]
|
||||
fn parse_keystore_key_types_mixed_short_and_long_forms() {
|
||||
let specs = vec![
|
||||
"audi".to_string(),
|
||||
"gran_sr".to_string(), // Override gran's default ed to sr
|
||||
"gran".to_string(),
|
||||
"beef".to_string(),
|
||||
];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
|
||||
assert_eq!(res.len(), 4);
|
||||
assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Sr)); // Overridden
|
||||
assert_eq!(res[2], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
assert_eq!(res[3], KeystoreKeyType::new("beef", KeyScheme::Ec));
|
||||
}
|
||||
assert_eq!(res.len(), 4);
|
||||
assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Sr)); // Overridden
|
||||
assert_eq!(res[2], KeystoreKeyType::new("gran", KeyScheme::Ed));
|
||||
assert_eq!(res[3], KeystoreKeyType::new("beef", KeyScheme::Ec));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_keystore_key_types_returns_defaults_when_empty() {
|
||||
let specs: Vec<String> = vec![];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
#[test]
|
||||
fn parse_keystore_key_types_returns_defaults_when_empty() {
|
||||
let specs: Vec<String> = vec![];
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
|
||||
// Should return all default keys
|
||||
assert!(!res.is_empty());
|
||||
assert!(res.iter().any(|k| k.key_type == "aura"));
|
||||
assert!(res.iter().any(|k| k.key_type == "babe"));
|
||||
assert!(res.iter().any(|k| k.key_type == "gran"));
|
||||
}
|
||||
// Should return all default keys
|
||||
assert!(!res.is_empty());
|
||||
assert!(res.iter().any(|k| k.key_type == "aura"));
|
||||
assert!(res.iter().any(|k| k.key_type == "babe"));
|
||||
assert!(res.iter().any(|k| k.key_type == "gran"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_keystore_key_types_allows_custom_key_with_explicit_scheme() {
|
||||
let specs = vec![
|
||||
"cust_sr".to_string(), // Custom key with explicit scheme
|
||||
"audi".to_string(),
|
||||
];
|
||||
let result = parse_keystore_key_types(&specs, false);
|
||||
#[test]
|
||||
fn parse_keystore_key_types_allows_custom_key_with_explicit_scheme() {
|
||||
let specs = vec![
|
||||
"cust_sr".to_string(), // Custom key with explicit scheme
|
||||
"audi".to_string(),
|
||||
];
|
||||
let result = parse_keystore_key_types(&specs, false);
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert_eq!(result[0], KeystoreKeyType::new("cust", KeyScheme::Sr));
|
||||
assert_eq!(result[1], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
}
|
||||
assert_eq!(result.len(), 2);
|
||||
assert_eq!(result[0], KeystoreKeyType::new("cust", KeyScheme::Sr));
|
||||
assert_eq!(result[1], KeystoreKeyType::new("audi", KeyScheme::Sr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_workflow_asset_hub_polkadot() {
|
||||
// For asset-hub-polkadot, aura should default to ed
|
||||
let specs = vec!["aura".to_string(), "babe".to_string()];
|
||||
#[test]
|
||||
fn full_workflow_asset_hub_polkadot() {
|
||||
// For asset-hub-polkadot, aura should default to ed
|
||||
let specs = vec!["aura".to_string(), "babe".to_string()];
|
||||
|
||||
let res = parse_keystore_key_types(&specs, true);
|
||||
let res = parse_keystore_key_types(&specs, true);
|
||||
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0].key_type, "aura");
|
||||
assert_eq!(res[0].scheme, KeyScheme::Ed); // ed for asset-hub-polkadot
|
||||
assert_eq!(res.len(), 2);
|
||||
assert_eq!(res[0].key_type, "aura");
|
||||
assert_eq!(res[0].scheme, KeyScheme::Ed); // ed for asset-hub-polkadot
|
||||
|
||||
assert_eq!(res[1].key_type, "babe");
|
||||
assert_eq!(res[1].scheme, KeyScheme::Sr);
|
||||
}
|
||||
assert_eq!(res[1].key_type, "babe");
|
||||
assert_eq!(res[1].scheme, KeyScheme::Sr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_workflow_custom_key_types() {
|
||||
let specs = vec![
|
||||
"aura".to_string(), // Use default scheme
|
||||
"gran_sr".to_string(), // Override gran to use sr instead of ed
|
||||
"cust_ec".to_string(), // Custom key type with ecdsa
|
||||
];
|
||||
#[test]
|
||||
fn full_workflow_custom_key_types() {
|
||||
let specs = vec![
|
||||
"aura".to_string(), // Use default scheme
|
||||
"gran_sr".to_string(), // Override gran to use sr instead of ed
|
||||
"cust_ec".to_string(), // Custom key type with ecdsa
|
||||
];
|
||||
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
let res = parse_keystore_key_types(&specs, false);
|
||||
|
||||
assert_eq!(res.len(), 3);
|
||||
assert_eq!(res.len(), 3);
|
||||
|
||||
// aura uses default sr
|
||||
assert_eq!(res[0].key_type, "aura");
|
||||
assert_eq!(res[0].scheme, KeyScheme::Sr);
|
||||
// aura uses default sr
|
||||
assert_eq!(res[0].key_type, "aura");
|
||||
assert_eq!(res[0].scheme, KeyScheme::Sr);
|
||||
|
||||
// gran overridden to sr
|
||||
assert_eq!(res[1].key_type, "gran");
|
||||
assert_eq!(res[1].scheme, KeyScheme::Sr);
|
||||
// gran overridden to sr
|
||||
assert_eq!(res[1].key_type, "gran");
|
||||
assert_eq!(res[1].scheme, KeyScheme::Sr);
|
||||
|
||||
// custom key with ec
|
||||
assert_eq!(res[2].key_type, "cust");
|
||||
assert_eq!(res[2].scheme, KeyScheme::Ec);
|
||||
}
|
||||
// custom key with ec
|
||||
assert_eq!(res[2].key_type, "cust");
|
||||
assert_eq!(res[2].scheme, KeyScheme::Ec);
|
||||
}
|
||||
}
|
||||
|
||||
+113
-127
@@ -2,9 +2,9 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use configuration::types::CommandWithCustomArgs;
|
||||
use provider::{
|
||||
constants::NODE_CONFIG_DIR,
|
||||
types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile},
|
||||
DynNamespace,
|
||||
constants::NODE_CONFIG_DIR,
|
||||
types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile},
|
||||
DynNamespace,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use support::fs::FileSystem;
|
||||
@@ -15,151 +15,137 @@ use crate::ScopedFilesystem;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum ParaArtifactType {
|
||||
Wasm,
|
||||
State,
|
||||
Wasm,
|
||||
State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum ParaArtifactBuildOption {
|
||||
Path(String),
|
||||
Command(String),
|
||||
CommandWithCustomArgs(CommandWithCustomArgs),
|
||||
Path(String),
|
||||
Command(String),
|
||||
CommandWithCustomArgs(CommandWithCustomArgs),
|
||||
}
|
||||
|
||||
/// Parachain artifact (could be either the genesis state or genesis wasm)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ParaArtifact {
|
||||
artifact_type: ParaArtifactType,
|
||||
build_option: ParaArtifactBuildOption,
|
||||
artifact_path: Option<PathBuf>,
|
||||
// image to use for building the para artifact
|
||||
image: Option<String>,
|
||||
artifact_type: ParaArtifactType,
|
||||
build_option: ParaArtifactBuildOption,
|
||||
artifact_path: Option<PathBuf>,
|
||||
// image to use for building the para artifact
|
||||
image: Option<String>,
|
||||
}
|
||||
|
||||
impl ParaArtifact {
|
||||
pub(crate) fn new(
|
||||
artifact_type: ParaArtifactType,
|
||||
build_option: ParaArtifactBuildOption,
|
||||
) -> Self {
|
||||
Self {
|
||||
artifact_type,
|
||||
build_option,
|
||||
artifact_path: None,
|
||||
image: None,
|
||||
}
|
||||
}
|
||||
pub(crate) fn new(
|
||||
artifact_type: ParaArtifactType,
|
||||
build_option: ParaArtifactBuildOption,
|
||||
) -> Self {
|
||||
Self { artifact_type, build_option, artifact_path: None, image: None }
|
||||
}
|
||||
|
||||
pub(crate) fn image(mut self, image: Option<String>) -> Self {
|
||||
self.image = image;
|
||||
self
|
||||
}
|
||||
pub(crate) fn image(mut self, image: Option<String>) -> Self {
|
||||
self.image = image;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn artifact_path(&self) -> Option<&PathBuf> {
|
||||
self.artifact_path.as_ref()
|
||||
}
|
||||
pub(crate) fn artifact_path(&self) -> Option<&PathBuf> {
|
||||
self.artifact_path.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) async fn build<'a, T>(
|
||||
&mut self,
|
||||
chain_spec_path: Option<impl AsRef<Path>>,
|
||||
artifact_path: impl AsRef<Path>,
|
||||
ns: &DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
maybe_output_path: Option<PathBuf>,
|
||||
) -> Result<(), GeneratorError>
|
||||
where
|
||||
T: FileSystem,
|
||||
{
|
||||
let (cmd, custom_args) = match &self.build_option {
|
||||
ParaArtifactBuildOption::Path(path) => {
|
||||
let t = TransferedFile::new(PathBuf::from(path), artifact_path.as_ref().into());
|
||||
scoped_fs.copy_files(vec![&t]).await?;
|
||||
self.artifact_path = Some(artifact_path.as_ref().into());
|
||||
return Ok(()); // work done!
|
||||
},
|
||||
ParaArtifactBuildOption::Command(cmd) => (cmd, &vec![]),
|
||||
ParaArtifactBuildOption::CommandWithCustomArgs(cmd_with_custom_args) => {
|
||||
(
|
||||
&cmd_with_custom_args.cmd().as_str().to_string(),
|
||||
cmd_with_custom_args.args(),
|
||||
)
|
||||
// (cmd.cmd_as_str().to_string(), cmd.1)
|
||||
},
|
||||
};
|
||||
pub(crate) async fn build<'a, T>(
|
||||
&mut self,
|
||||
chain_spec_path: Option<impl AsRef<Path>>,
|
||||
artifact_path: impl AsRef<Path>,
|
||||
ns: &DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
maybe_output_path: Option<PathBuf>,
|
||||
) -> Result<(), GeneratorError>
|
||||
where
|
||||
T: FileSystem,
|
||||
{
|
||||
let (cmd, custom_args) = match &self.build_option {
|
||||
ParaArtifactBuildOption::Path(path) => {
|
||||
let t = TransferedFile::new(PathBuf::from(path), artifact_path.as_ref().into());
|
||||
scoped_fs.copy_files(vec![&t]).await?;
|
||||
self.artifact_path = Some(artifact_path.as_ref().into());
|
||||
return Ok(()); // work done!
|
||||
},
|
||||
ParaArtifactBuildOption::Command(cmd) => (cmd, &vec![]),
|
||||
ParaArtifactBuildOption::CommandWithCustomArgs(cmd_with_custom_args) => {
|
||||
(&cmd_with_custom_args.cmd().as_str().to_string(), cmd_with_custom_args.args())
|
||||
// (cmd.cmd_as_str().to_string(), cmd.1)
|
||||
},
|
||||
};
|
||||
|
||||
let generate_subcmd = match self.artifact_type {
|
||||
ParaArtifactType::Wasm => "export-genesis-wasm",
|
||||
ParaArtifactType::State => "export-genesis-state",
|
||||
};
|
||||
let generate_subcmd = match self.artifact_type {
|
||||
ParaArtifactType::Wasm => "export-genesis-wasm",
|
||||
ParaArtifactType::State => "export-genesis-state",
|
||||
};
|
||||
|
||||
// TODO: replace uuid with para_id-random
|
||||
let temp_name = format!("temp-{}-{}", generate_subcmd, Uuid::new_v4());
|
||||
let mut args: Vec<String> = vec![generate_subcmd.into()];
|
||||
// TODO: replace uuid with para_id-random
|
||||
let temp_name = format!("temp-{}-{}", generate_subcmd, Uuid::new_v4());
|
||||
let mut args: Vec<String> = vec![generate_subcmd.into()];
|
||||
|
||||
let files_to_inject = if let Some(chain_spec_path) = chain_spec_path {
|
||||
// TODO: we should get the full path from the scoped filesystem
|
||||
let chain_spec_path_local = format!(
|
||||
"{}/{}",
|
||||
ns.base_dir().to_string_lossy(),
|
||||
chain_spec_path.as_ref().to_string_lossy()
|
||||
);
|
||||
// Remote path to be injected
|
||||
let chain_spec_path_in_pod = format!(
|
||||
"{}/{}",
|
||||
NODE_CONFIG_DIR,
|
||||
chain_spec_path.as_ref().to_string_lossy()
|
||||
);
|
||||
// Path in the context of the node, this can be different in the context of the providers (e.g native)
|
||||
let chain_spec_path_in_args = if ns.capabilities().prefix_with_full_path {
|
||||
// In native
|
||||
format!(
|
||||
"{}/{}{}",
|
||||
ns.base_dir().to_string_lossy(),
|
||||
&temp_name,
|
||||
&chain_spec_path_in_pod
|
||||
)
|
||||
} else {
|
||||
chain_spec_path_in_pod.clone()
|
||||
};
|
||||
let files_to_inject = if let Some(chain_spec_path) = chain_spec_path {
|
||||
// TODO: we should get the full path from the scoped filesystem
|
||||
let chain_spec_path_local = format!(
|
||||
"{}/{}",
|
||||
ns.base_dir().to_string_lossy(),
|
||||
chain_spec_path.as_ref().to_string_lossy()
|
||||
);
|
||||
// Remote path to be injected
|
||||
let chain_spec_path_in_pod =
|
||||
format!("{}/{}", NODE_CONFIG_DIR, chain_spec_path.as_ref().to_string_lossy());
|
||||
// Path in the context of the node, this can be different in the context of the providers (e.g native)
|
||||
let chain_spec_path_in_args = if ns.capabilities().prefix_with_full_path {
|
||||
// In native
|
||||
format!(
|
||||
"{}/{}{}",
|
||||
ns.base_dir().to_string_lossy(),
|
||||
&temp_name,
|
||||
&chain_spec_path_in_pod
|
||||
)
|
||||
} else {
|
||||
chain_spec_path_in_pod.clone()
|
||||
};
|
||||
|
||||
args.push("--chain".into());
|
||||
args.push(chain_spec_path_in_args);
|
||||
args.push("--chain".into());
|
||||
args.push(chain_spec_path_in_args);
|
||||
|
||||
for custom_arg in custom_args {
|
||||
match custom_arg {
|
||||
configuration::types::Arg::Flag(flag) => {
|
||||
args.push(flag.into());
|
||||
},
|
||||
configuration::types::Arg::Option(flag, flag_value) => {
|
||||
args.push(flag.into());
|
||||
args.push(flag_value.into());
|
||||
},
|
||||
configuration::types::Arg::Array(flag, values) => {
|
||||
args.push(flag.into());
|
||||
values.iter().for_each(|v| args.push(v.into()));
|
||||
},
|
||||
}
|
||||
}
|
||||
for custom_arg in custom_args {
|
||||
match custom_arg {
|
||||
configuration::types::Arg::Flag(flag) => {
|
||||
args.push(flag.into());
|
||||
},
|
||||
configuration::types::Arg::Option(flag, flag_value) => {
|
||||
args.push(flag.into());
|
||||
args.push(flag_value.into());
|
||||
},
|
||||
configuration::types::Arg::Array(flag, values) => {
|
||||
args.push(flag.into());
|
||||
values.iter().for_each(|v| args.push(v.into()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
vec![TransferedFile::new(
|
||||
chain_spec_path_local,
|
||||
chain_spec_path_in_pod,
|
||||
)]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
vec![TransferedFile::new(chain_spec_path_local, chain_spec_path_in_pod)]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let artifact_path_ref = artifact_path.as_ref();
|
||||
let generate_command = GenerateFileCommand::new(cmd.as_str(), artifact_path_ref).args(args);
|
||||
let options = GenerateFilesOptions::with_files(
|
||||
vec![generate_command],
|
||||
self.image.clone(),
|
||||
&files_to_inject,
|
||||
maybe_output_path,
|
||||
)
|
||||
.temp_name(temp_name);
|
||||
ns.generate_files(options).await?;
|
||||
self.artifact_path = Some(artifact_path_ref.into());
|
||||
let artifact_path_ref = artifact_path.as_ref();
|
||||
let generate_command = GenerateFileCommand::new(cmd.as_str(), artifact_path_ref).args(args);
|
||||
let options = GenerateFilesOptions::with_files(
|
||||
vec![generate_command],
|
||||
self.image.clone(),
|
||||
&files_to_inject,
|
||||
maybe_output_path,
|
||||
)
|
||||
.temp_name(temp_name);
|
||||
ns.generate_files(options).await?;
|
||||
self.artifact_path = Some(artifact_path_ref.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,41 +8,39 @@ use crate::shared::types::ParkedPort;
|
||||
|
||||
// TODO: (team), we want to continue support ws_port? No
|
||||
enum PortTypes {
|
||||
Rpc,
|
||||
P2P,
|
||||
Prometheus,
|
||||
Rpc,
|
||||
P2P,
|
||||
Prometheus,
|
||||
}
|
||||
|
||||
pub fn generate(port: Option<Port>) -> Result<ParkedPort, GeneratorError> {
|
||||
let port = port.unwrap_or(0);
|
||||
let listener = TcpListener::bind(format!("0.0.0.0:{port}"))
|
||||
.map_err(|_e| GeneratorError::PortGeneration(port, "Can't bind".into()))?;
|
||||
let port = listener
|
||||
.local_addr()
|
||||
.expect(&format!(
|
||||
"We should always get the local_addr from the listener {THIS_IS_A_BUG}"
|
||||
))
|
||||
.port();
|
||||
Ok(ParkedPort::new(port, listener))
|
||||
let port = port.unwrap_or(0);
|
||||
let listener = TcpListener::bind(format!("0.0.0.0:{port}"))
|
||||
.map_err(|_e| GeneratorError::PortGeneration(port, "Can't bind".into()))?;
|
||||
let port = listener
|
||||
.local_addr()
|
||||
.expect(&format!("We should always get the local_addr from the listener {THIS_IS_A_BUG}"))
|
||||
.port();
|
||||
Ok(ParkedPort::new(port, listener))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_random() {
|
||||
let port = generate(None).unwrap();
|
||||
let listener = port.1.write().unwrap();
|
||||
use super::*;
|
||||
#[test]
|
||||
fn generate_random() {
|
||||
let port = generate(None).unwrap();
|
||||
let listener = port.1.write().unwrap();
|
||||
|
||||
assert!(listener.is_some());
|
||||
}
|
||||
assert!(listener.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_fixed_port() {
|
||||
let port = generate(Some(33056)).unwrap();
|
||||
let listener = port.1.write().unwrap();
|
||||
#[test]
|
||||
fn generate_fixed_port() {
|
||||
let port = generate(Some(33056)).unwrap();
|
||||
let listener = port.1.write().unwrap();
|
||||
|
||||
assert!(listener.is_some());
|
||||
assert_eq!(port.0, 33056);
|
||||
}
|
||||
assert!(listener.is_some());
|
||||
assert_eq!(port.0, 33056);
|
||||
}
|
||||
}
|
||||
|
||||
+1055
-1138
File diff suppressed because it is too large
Load Diff
+760
-808
File diff suppressed because it is too large
Load Diff
+25
-25
@@ -9,33 +9,33 @@ use crate::{shared::types::RuntimeUpgradeOptions, tx_helper};
|
||||
|
||||
#[async_trait]
|
||||
pub trait ChainUpgrade {
|
||||
/// Perform a runtime upgrade (with sudo)
|
||||
///
|
||||
/// This call 'System.set_code_without_checks' wrapped in
|
||||
/// 'Sudo.sudo_unchecked_weight'
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error>;
|
||||
/// Perform a runtime upgrade (with sudo)
|
||||
///
|
||||
/// This call 'System.set_code_without_checks' wrapped in
|
||||
/// 'Sudo.sudo_unchecked_weight'
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error>;
|
||||
|
||||
/// Perform a runtime upgrade (with sudo), inner call with the node pass as arg.
|
||||
///
|
||||
/// This call 'System.set_code_without_checks' wrapped in
|
||||
/// 'Sudo.sudo_unchecked_weight'
|
||||
async fn perform_runtime_upgrade(
|
||||
&self,
|
||||
node: &NetworkNode,
|
||||
options: RuntimeUpgradeOptions,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let sudo = if let Some(possible_seed) = options.seed {
|
||||
Keypair::from_secret_key(possible_seed)
|
||||
.map_err(|_| anyhow!("seed should return a Keypair"))?
|
||||
} else {
|
||||
let uri = SecretUri::from_str("//Alice")?;
|
||||
Keypair::from_uri(&uri).map_err(|_| anyhow!("'//Alice' should return a Keypair"))?
|
||||
};
|
||||
/// Perform a runtime upgrade (with sudo), inner call with the node pass as arg.
|
||||
///
|
||||
/// This call 'System.set_code_without_checks' wrapped in
|
||||
/// 'Sudo.sudo_unchecked_weight'
|
||||
async fn perform_runtime_upgrade(
|
||||
&self,
|
||||
node: &NetworkNode,
|
||||
options: RuntimeUpgradeOptions,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let sudo = if let Some(possible_seed) = options.seed {
|
||||
Keypair::from_secret_key(possible_seed)
|
||||
.map_err(|_| anyhow!("seed should return a Keypair"))?
|
||||
} else {
|
||||
let uri = SecretUri::from_str("//Alice")?;
|
||||
Keypair::from_uri(&uri).map_err(|_| anyhow!("'//Alice' should return a Keypair"))?
|
||||
};
|
||||
|
||||
let wasm_data = options.wasm.get_asset().await?;
|
||||
let wasm_data = options.wasm.get_asset().await?;
|
||||
|
||||
tx_helper::runtime_upgrade::upgrade(node, &wasm_data, &sudo).await?;
|
||||
tx_helper::runtime_upgrade::upgrade(node, &wasm_data, &sudo).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+962
-983
File diff suppressed because it is too large
Load Diff
+39
-48
@@ -6,70 +6,61 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::node::NetworkNode;
|
||||
use crate::{
|
||||
network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions,
|
||||
utils::default_as_empty_vec,
|
||||
network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions,
|
||||
utils::default_as_empty_vec,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Relaychain {
|
||||
pub(crate) chain: String,
|
||||
pub(crate) chain_id: String,
|
||||
pub(crate) chain_spec_path: PathBuf,
|
||||
#[serde(default, deserialize_with = "default_as_empty_vec")]
|
||||
pub(crate) nodes: Vec<NetworkNode>,
|
||||
pub(crate) chain: String,
|
||||
pub(crate) chain_id: String,
|
||||
pub(crate) chain_spec_path: PathBuf,
|
||||
#[serde(default, deserialize_with = "default_as_empty_vec")]
|
||||
pub(crate) nodes: Vec<NetworkNode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct RawRelaychain {
|
||||
#[serde(flatten)]
|
||||
pub(crate) inner: Relaychain,
|
||||
pub(crate) nodes: serde_json::Value,
|
||||
#[serde(flatten)]
|
||||
pub(crate) inner: Relaychain,
|
||||
pub(crate) nodes: serde_json::Value,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ChainUpgrade for Relaychain {
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> {
|
||||
// check if the node is valid first
|
||||
let node = if let Some(node_name) = &options.node_name {
|
||||
if let Some(node) = self
|
||||
.nodes()
|
||||
.into_iter()
|
||||
.find(|node| node.name() == node_name)
|
||||
{
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("Node: {node_name} is not part of the set of nodes"));
|
||||
}
|
||||
} else {
|
||||
// take the first node
|
||||
if let Some(node) = self.nodes().first() {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("chain doesn't have any node!"));
|
||||
}
|
||||
};
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> {
|
||||
// check if the node is valid first
|
||||
let node = if let Some(node_name) = &options.node_name {
|
||||
if let Some(node) = self.nodes().into_iter().find(|node| node.name() == node_name) {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("Node: {node_name} is not part of the set of nodes"));
|
||||
}
|
||||
} else {
|
||||
// take the first node
|
||||
if let Some(node) = self.nodes().first() {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("chain doesn't have any node!"));
|
||||
}
|
||||
};
|
||||
|
||||
self.perform_runtime_upgrade(node, options).await
|
||||
}
|
||||
self.perform_runtime_upgrade(node, options).await
|
||||
}
|
||||
}
|
||||
|
||||
impl Relaychain {
|
||||
pub(crate) fn new(chain: String, chain_id: String, chain_spec_path: PathBuf) -> Self {
|
||||
Self {
|
||||
chain,
|
||||
chain_id,
|
||||
chain_spec_path,
|
||||
nodes: Default::default(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn new(chain: String, chain_id: String, chain_spec_path: PathBuf) -> Self {
|
||||
Self { chain, chain_id, chain_spec_path, nodes: Default::default() }
|
||||
}
|
||||
|
||||
// Public API
|
||||
pub fn nodes(&self) -> Vec<&NetworkNode> {
|
||||
self.nodes.iter().collect()
|
||||
}
|
||||
// Public API
|
||||
pub fn nodes(&self) -> Vec<&NetworkNode> {
|
||||
self.nodes.iter().collect()
|
||||
}
|
||||
|
||||
/// Get chain name
|
||||
pub fn chain(&self) -> &str {
|
||||
&self.chain
|
||||
}
|
||||
/// Get chain name
|
||||
pub fn chain(&self) -> &str {
|
||||
&self.chain
|
||||
}
|
||||
}
|
||||
|
||||
+229
-264
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
@@ -14,317 +14,282 @@ use tracing::info;
|
||||
|
||||
use super::{chain_upgrade::ChainUpgrade, node::NetworkNode};
|
||||
use crate::{
|
||||
network_spec::teyrchain::TeyrchainSpec,
|
||||
shared::types::{RegisterParachainOptions, RuntimeUpgradeOptions},
|
||||
tx_helper::client::get_client_from_url,
|
||||
utils::default_as_empty_vec,
|
||||
ScopedFilesystem,
|
||||
network_spec::teyrchain::TeyrchainSpec,
|
||||
shared::types::{RegisterParachainOptions, RuntimeUpgradeOptions},
|
||||
tx_helper::client::get_client_from_url,
|
||||
utils::default_as_empty_vec,
|
||||
ScopedFilesystem,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Parachain {
|
||||
pub(crate) chain: Option<String>,
|
||||
pub(crate) para_id: u32,
|
||||
// unique_id is internally used to allow multiple parachains with the same id
|
||||
// See `ParachainConfig` for more details
|
||||
pub(crate) unique_id: String,
|
||||
pub(crate) chain_id: Option<String>,
|
||||
pub(crate) chain_spec_path: Option<PathBuf>,
|
||||
#[serde(default, deserialize_with = "default_as_empty_vec")]
|
||||
pub(crate) collators: Vec<NetworkNode>,
|
||||
pub(crate) files_to_inject: Vec<TransferedFile>,
|
||||
pub(crate) bootnodes_addresses: Vec<multiaddr::Multiaddr>,
|
||||
pub(crate) chain: Option<String>,
|
||||
pub(crate) para_id: u32,
|
||||
// unique_id is internally used to allow multiple parachains with the same id
|
||||
// See `ParachainConfig` for more details
|
||||
pub(crate) unique_id: String,
|
||||
pub(crate) chain_id: Option<String>,
|
||||
pub(crate) chain_spec_path: Option<PathBuf>,
|
||||
#[serde(default, deserialize_with = "default_as_empty_vec")]
|
||||
pub(crate) collators: Vec<NetworkNode>,
|
||||
pub(crate) files_to_inject: Vec<TransferedFile>,
|
||||
pub(crate) bootnodes_addresses: Vec<multiaddr::Multiaddr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct RawParachain {
|
||||
#[serde(flatten)]
|
||||
pub(crate) inner: Parachain,
|
||||
pub(crate) collators: serde_json::Value,
|
||||
#[serde(flatten)]
|
||||
pub(crate) inner: Parachain,
|
||||
pub(crate) collators: serde_json::Value,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ChainUpgrade for Parachain {
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> {
|
||||
// check if the node is valid first
|
||||
let node = if let Some(node_name) = &options.node_name {
|
||||
if let Some(node) = self
|
||||
.collators()
|
||||
.into_iter()
|
||||
.find(|node| node.name() == node_name)
|
||||
{
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("Node: {node_name} is not part of the set of nodes"));
|
||||
}
|
||||
} else {
|
||||
// take the first node
|
||||
if let Some(node) = self.collators().first() {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("chain doesn't have any node!"));
|
||||
}
|
||||
};
|
||||
async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> {
|
||||
// check if the node is valid first
|
||||
let node = if let Some(node_name) = &options.node_name {
|
||||
if let Some(node) = self.collators().into_iter().find(|node| node.name() == node_name) {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("Node: {node_name} is not part of the set of nodes"));
|
||||
}
|
||||
} else {
|
||||
// take the first node
|
||||
if let Some(node) = self.collators().first() {
|
||||
node
|
||||
} else {
|
||||
return Err(anyhow!("chain doesn't have any node!"));
|
||||
}
|
||||
};
|
||||
|
||||
self.perform_runtime_upgrade(node, options).await
|
||||
}
|
||||
self.perform_runtime_upgrade(node, options).await
|
||||
}
|
||||
}
|
||||
|
||||
impl Parachain {
|
||||
pub(crate) fn new(para_id: u32, unique_id: impl Into<String>) -> Self {
|
||||
Self {
|
||||
chain: None,
|
||||
para_id,
|
||||
unique_id: unique_id.into(),
|
||||
chain_id: None,
|
||||
chain_spec_path: None,
|
||||
collators: Default::default(),
|
||||
files_to_inject: Default::default(),
|
||||
bootnodes_addresses: vec![],
|
||||
}
|
||||
}
|
||||
pub(crate) fn new(para_id: u32, unique_id: impl Into<String>) -> Self {
|
||||
Self {
|
||||
chain: None,
|
||||
para_id,
|
||||
unique_id: unique_id.into(),
|
||||
chain_id: None,
|
||||
chain_spec_path: None,
|
||||
collators: Default::default(),
|
||||
files_to_inject: Default::default(),
|
||||
bootnodes_addresses: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_chain_spec(
|
||||
para_id: u32,
|
||||
unique_id: impl Into<String>,
|
||||
chain_id: impl Into<String>,
|
||||
chain_spec_path: impl AsRef<Path>,
|
||||
) -> Self {
|
||||
Self {
|
||||
para_id,
|
||||
unique_id: unique_id.into(),
|
||||
chain: None,
|
||||
chain_id: Some(chain_id.into()),
|
||||
chain_spec_path: Some(chain_spec_path.as_ref().into()),
|
||||
collators: Default::default(),
|
||||
files_to_inject: Default::default(),
|
||||
bootnodes_addresses: vec![],
|
||||
}
|
||||
}
|
||||
pub(crate) fn with_chain_spec(
|
||||
para_id: u32,
|
||||
unique_id: impl Into<String>,
|
||||
chain_id: impl Into<String>,
|
||||
chain_spec_path: impl AsRef<Path>,
|
||||
) -> Self {
|
||||
Self {
|
||||
para_id,
|
||||
unique_id: unique_id.into(),
|
||||
chain: None,
|
||||
chain_id: Some(chain_id.into()),
|
||||
chain_spec_path: Some(chain_spec_path.as_ref().into()),
|
||||
collators: Default::default(),
|
||||
files_to_inject: Default::default(),
|
||||
bootnodes_addresses: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn from_spec(
|
||||
para: &TeyrchainSpec,
|
||||
files_to_inject: &[TransferedFile],
|
||||
scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let mut para_files_to_inject = files_to_inject.to_owned();
|
||||
pub(crate) async fn from_spec(
|
||||
para: &TeyrchainSpec,
|
||||
files_to_inject: &[TransferedFile],
|
||||
scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let mut para_files_to_inject = files_to_inject.to_owned();
|
||||
|
||||
// parachain id is used for the keystore
|
||||
let mut parachain = if let Some(chain_spec) = para.chain_spec.as_ref() {
|
||||
let id = chain_spec.read_chain_id(scoped_fs).await?;
|
||||
// parachain id is used for the keystore
|
||||
let mut parachain = if let Some(chain_spec) = para.chain_spec.as_ref() {
|
||||
let id = chain_spec.read_chain_id(scoped_fs).await?;
|
||||
|
||||
// add the spec to global files to inject
|
||||
let spec_name = chain_spec.chain_spec_name();
|
||||
let base = PathBuf::from_str(scoped_fs.base_dir)?;
|
||||
para_files_to_inject.push(TransferedFile::new(
|
||||
base.join(format!("{spec_name}.json")),
|
||||
PathBuf::from(format!("/cfg/{}.json", para.id)),
|
||||
));
|
||||
// add the spec to global files to inject
|
||||
let spec_name = chain_spec.chain_spec_name();
|
||||
let base = PathBuf::from_str(scoped_fs.base_dir)?;
|
||||
para_files_to_inject.push(TransferedFile::new(
|
||||
base.join(format!("{spec_name}.json")),
|
||||
PathBuf::from(format!("/cfg/{}.json", para.id)),
|
||||
));
|
||||
|
||||
let raw_path = chain_spec
|
||||
.raw_path()
|
||||
.ok_or(anyhow::anyhow!("chain-spec path should be set by now.",))?;
|
||||
let mut running_para =
|
||||
Parachain::with_chain_spec(para.id, ¶.unique_id, id, raw_path);
|
||||
if let Some(chain_name) = chain_spec.chain_name() {
|
||||
running_para.chain = Some(chain_name.to_string());
|
||||
}
|
||||
let raw_path = chain_spec
|
||||
.raw_path()
|
||||
.ok_or(anyhow::anyhow!("chain-spec path should be set by now.",))?;
|
||||
let mut running_para =
|
||||
Parachain::with_chain_spec(para.id, ¶.unique_id, id, raw_path);
|
||||
if let Some(chain_name) = chain_spec.chain_name() {
|
||||
running_para.chain = Some(chain_name.to_string());
|
||||
}
|
||||
|
||||
running_para
|
||||
} else {
|
||||
Parachain::new(para.id, ¶.unique_id)
|
||||
};
|
||||
running_para
|
||||
} else {
|
||||
Parachain::new(para.id, ¶.unique_id)
|
||||
};
|
||||
|
||||
parachain.bootnodes_addresses = para.bootnodes_addresses().into_iter().cloned().collect();
|
||||
parachain.files_to_inject = para_files_to_inject;
|
||||
parachain.bootnodes_addresses = para.bootnodes_addresses().into_iter().cloned().collect();
|
||||
parachain.files_to_inject = para_files_to_inject;
|
||||
|
||||
Ok(parachain)
|
||||
}
|
||||
Ok(parachain)
|
||||
}
|
||||
|
||||
pub async fn register(
|
||||
options: RegisterParachainOptions,
|
||||
scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
info!("Registering parachain: {:?}", options);
|
||||
// get the seed
|
||||
let sudo: Keypair;
|
||||
if let Some(possible_seed) = options.seed {
|
||||
sudo = Keypair::from_secret_key(possible_seed)
|
||||
.expect(&format!("seed should return a Keypair {THIS_IS_A_BUG}"));
|
||||
} else {
|
||||
let uri = SecretUri::from_str("//Alice")?;
|
||||
sudo = Keypair::from_uri(&uri)?;
|
||||
}
|
||||
pub async fn register(
|
||||
options: RegisterParachainOptions,
|
||||
scoped_fs: &ScopedFilesystem<'_, impl FileSystem>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
info!("Registering parachain: {:?}", options);
|
||||
// get the seed
|
||||
let sudo: Keypair;
|
||||
if let Some(possible_seed) = options.seed {
|
||||
sudo = Keypair::from_secret_key(possible_seed)
|
||||
.expect(&format!("seed should return a Keypair {THIS_IS_A_BUG}"));
|
||||
} else {
|
||||
let uri = SecretUri::from_str("//Alice")?;
|
||||
sudo = Keypair::from_uri(&uri)?;
|
||||
}
|
||||
|
||||
let genesis_state = scoped_fs
|
||||
.read_to_string(options.state_path)
|
||||
.await
|
||||
.expect(&format!(
|
||||
"State Path should be ok by this point {THIS_IS_A_BUG}"
|
||||
));
|
||||
let wasm_data = scoped_fs
|
||||
.read_to_string(options.wasm_path)
|
||||
.await
|
||||
.expect(&format!(
|
||||
"Wasm Path should be ok by this point {THIS_IS_A_BUG}"
|
||||
));
|
||||
let genesis_state = scoped_fs
|
||||
.read_to_string(options.state_path)
|
||||
.await
|
||||
.expect(&format!("State Path should be ok by this point {THIS_IS_A_BUG}"));
|
||||
let wasm_data = scoped_fs
|
||||
.read_to_string(options.wasm_path)
|
||||
.await
|
||||
.expect(&format!("Wasm Path should be ok by this point {THIS_IS_A_BUG}"));
|
||||
|
||||
wait_ws_ready(options.node_ws_url.as_str())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"Error waiting for ws to be ready, at {}",
|
||||
options.node_ws_url.as_str()
|
||||
)
|
||||
})?;
|
||||
wait_ws_ready(options.node_ws_url.as_str()).await.map_err(|_| {
|
||||
anyhow::anyhow!("Error waiting for ws to be ready, at {}", options.node_ws_url.as_str())
|
||||
})?;
|
||||
|
||||
let api: OnlineClient<BizinikiwConfig> = get_client_from_url(&options.node_ws_url).await?;
|
||||
let api: OnlineClient<BizinikiwConfig> = get_client_from_url(&options.node_ws_url).await?;
|
||||
|
||||
let schedule_para = pezkuwi_subxt::dynamic::tx(
|
||||
"ParasSudoWrapper",
|
||||
"sudo_schedule_para_initialize",
|
||||
vec![
|
||||
Value::primitive(options.id.into()),
|
||||
Value::named_composite([
|
||||
(
|
||||
"genesis_head",
|
||||
Value::from_bytes(hex::decode(&genesis_state[2..])?),
|
||||
),
|
||||
(
|
||||
"validation_code",
|
||||
Value::from_bytes(hex::decode(&wasm_data[2..])?),
|
||||
),
|
||||
("para_kind", Value::bool(options.onboard_as_para)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
let schedule_para = pezkuwi_subxt::dynamic::tx(
|
||||
"ParasSudoWrapper",
|
||||
"sudo_schedule_para_initialize",
|
||||
vec![
|
||||
Value::primitive(options.id.into()),
|
||||
Value::named_composite([
|
||||
("genesis_head", Value::from_bytes(hex::decode(&genesis_state[2..])?)),
|
||||
("validation_code", Value::from_bytes(hex::decode(&wasm_data[2..])?)),
|
||||
("para_kind", Value::bool(options.onboard_as_para)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
let sudo_call =
|
||||
pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![schedule_para.into_value()]);
|
||||
let sudo_call =
|
||||
pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![schedule_para.into_value()]);
|
||||
|
||||
// TODO: uncomment below and fix the sign and submit (and follow afterwards until
|
||||
// finalized block) to register the parachain
|
||||
let mut tx = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&sudo_call, &sudo)
|
||||
.await?;
|
||||
// TODO: uncomment below and fix the sign and submit (and follow afterwards until
|
||||
// finalized block) to register the parachain
|
||||
let mut tx = api.tx().sign_and_submit_then_watch_default(&sudo_call, &sudo).await?;
|
||||
|
||||
// Below we use the low level API to replicate the `wait_for_in_block` behaviour
|
||||
// which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237.
|
||||
while let Some(status) = tx.next().await {
|
||||
match status? {
|
||||
TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => {
|
||||
let _result = tx_in_block.wait_for_success().await?;
|
||||
info!("In block: {:#?}", tx_in_block.block_hash());
|
||||
},
|
||||
TxStatus::Error { message }
|
||||
| TxStatus::Invalid { message }
|
||||
| TxStatus::Dropped { message } => {
|
||||
return Err(anyhow::format_err!("Error submitting tx: {message}"));
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
// Below we use the low level API to replicate the `wait_for_in_block` behaviour
|
||||
// which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237.
|
||||
while let Some(status) = tx.next().await {
|
||||
match status? {
|
||||
TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => {
|
||||
let _result = tx_in_block.wait_for_success().await?;
|
||||
info!("In block: {:#?}", tx_in_block.block_hash());
|
||||
},
|
||||
TxStatus::Error { message }
|
||||
| TxStatus::Invalid { message }
|
||||
| TxStatus::Dropped { message } => {
|
||||
return Err(anyhow::format_err!("Error submitting tx: {message}"));
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn para_id(&self) -> u32 {
|
||||
self.para_id
|
||||
}
|
||||
pub fn para_id(&self) -> u32 {
|
||||
self.para_id
|
||||
}
|
||||
|
||||
pub fn unique_id(&self) -> &str {
|
||||
self.unique_id.as_str()
|
||||
}
|
||||
pub fn unique_id(&self) -> &str {
|
||||
self.unique_id.as_str()
|
||||
}
|
||||
|
||||
pub fn chain_id(&self) -> Option<&str> {
|
||||
self.chain_id.as_deref()
|
||||
}
|
||||
pub fn chain_id(&self) -> Option<&str> {
|
||||
self.chain_id.as_deref()
|
||||
}
|
||||
|
||||
pub fn collators(&self) -> Vec<&NetworkNode> {
|
||||
self.collators.iter().collect()
|
||||
}
|
||||
pub fn collators(&self) -> Vec<&NetworkNode> {
|
||||
self.collators.iter().collect()
|
||||
}
|
||||
|
||||
pub fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> {
|
||||
self.bootnodes_addresses.iter().collect()
|
||||
}
|
||||
pub fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> {
|
||||
self.bootnodes_addresses.iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create_with_is_works() {
|
||||
let para = Parachain::new(100, "100");
|
||||
// only para_id and unique_id should be set
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, None);
|
||||
assert_eq!(para.chain, None);
|
||||
assert_eq!(para.chain_spec_path, None);
|
||||
}
|
||||
#[test]
|
||||
fn create_with_is_works() {
|
||||
let para = Parachain::new(100, "100");
|
||||
// only para_id and unique_id should be set
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, None);
|
||||
assert_eq!(para.chain, None);
|
||||
assert_eq!(para.chain_spec_path, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_with_chain_spec_works() {
|
||||
let para = Parachain::with_chain_spec(100, "100", "rococo-local", "/tmp/rococo-local.json");
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, Some("rococo-local".to_string()));
|
||||
assert_eq!(para.chain, None);
|
||||
assert_eq!(
|
||||
para.chain_spec_path,
|
||||
Some(PathBuf::from("/tmp/rococo-local.json"))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn create_with_chain_spec_works() {
|
||||
let para = Parachain::with_chain_spec(100, "100", "rococo-local", "/tmp/rococo-local.json");
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, Some("rococo-local".to_string()));
|
||||
assert_eq!(para.chain, None);
|
||||
assert_eq!(para.chain_spec_path, Some(PathBuf::from("/tmp/rococo-local.json")));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_with_para_spec_works() {
|
||||
use configuration::ParachainConfigBuilder;
|
||||
#[tokio::test]
|
||||
async fn create_with_para_spec_works() {
|
||||
use configuration::ParachainConfigBuilder;
|
||||
|
||||
use crate::network_spec::teyrchain::TeyrchainSpec;
|
||||
use crate::network_spec::teyrchain::TeyrchainSpec;
|
||||
|
||||
let bootnode_addresses = vec!["/ip4/10.41.122.55/tcp/45421"];
|
||||
let bootnode_addresses = vec!["/ip4/10.41.122.55/tcp/45421"];
|
||||
|
||||
let para_config = ParachainConfigBuilder::new(Default::default())
|
||||
.with_id(100)
|
||||
.cumulus_based(false)
|
||||
.with_default_command("adder-collator")
|
||||
.with_raw_bootnodes_addresses(bootnode_addresses.clone())
|
||||
.with_collator(|c| c.with_name("col"))
|
||||
.build()
|
||||
.unwrap();
|
||||
let para_config = ParachainConfigBuilder::new(Default::default())
|
||||
.with_id(100)
|
||||
.cumulus_based(false)
|
||||
.with_default_command("adder-collator")
|
||||
.with_raw_bootnodes_addresses(bootnode_addresses.clone())
|
||||
.with_collator(|c| c.with_name("col"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let para_spec =
|
||||
TeyrchainSpec::from_config(¶_config, "rococo-local".try_into().unwrap()).unwrap();
|
||||
let fs = support::fs::in_memory::InMemoryFileSystem::new(HashMap::default());
|
||||
let scoped_fs = ScopedFilesystem {
|
||||
fs: &fs,
|
||||
base_dir: "/tmp/some",
|
||||
};
|
||||
let para_spec =
|
||||
TeyrchainSpec::from_config(¶_config, "rococo-local".try_into().unwrap()).unwrap();
|
||||
let fs = support::fs::in_memory::InMemoryFileSystem::new(HashMap::default());
|
||||
let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/some" };
|
||||
|
||||
let files = vec![TransferedFile::new(
|
||||
PathBuf::from("/tmp/some"),
|
||||
PathBuf::from("/tmp/some"),
|
||||
)];
|
||||
let para = Parachain::from_spec(¶_spec, &files, &scoped_fs)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("{para:#?}");
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, None);
|
||||
assert_eq!(para.chain, None);
|
||||
// one file should be added.
|
||||
assert_eq!(para.files_to_inject.len(), 1);
|
||||
assert_eq!(
|
||||
para.bootnodes_addresses()
|
||||
.iter()
|
||||
.map(|addr| addr.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
bootnode_addresses
|
||||
);
|
||||
}
|
||||
let files =
|
||||
vec![TransferedFile::new(PathBuf::from("/tmp/some"), PathBuf::from("/tmp/some"))];
|
||||
let para = Parachain::from_spec(¶_spec, &files, &scoped_fs).await.unwrap();
|
||||
println!("{para:#?}");
|
||||
assert_eq!(para.para_id, 100);
|
||||
assert_eq!(para.unique_id, "100");
|
||||
assert_eq!(para.chain_id, None);
|
||||
assert_eq!(para.chain, None);
|
||||
// one file should be added.
|
||||
assert_eq!(para.files_to_inject.len(), 1);
|
||||
assert_eq!(
|
||||
para.bootnodes_addresses().iter().map(|addr| addr.to_string()).collect::<Vec<_>>(),
|
||||
bootnode_addresses
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-41
@@ -5,58 +5,56 @@ use reqwest::Url;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MetricsHelper {
|
||||
async fn metric(&self, metric_name: &str) -> Result<f64, anyhow::Error>;
|
||||
async fn metric_with_url(
|
||||
metric: impl AsRef<str> + Send,
|
||||
endpoint: impl Into<Url> + Send,
|
||||
) -> Result<f64, anyhow::Error>;
|
||||
async fn metric(&self, metric_name: &str) -> Result<f64, anyhow::Error>;
|
||||
async fn metric_with_url(
|
||||
metric: impl AsRef<str> + Send,
|
||||
endpoint: impl Into<Url> + Send,
|
||||
) -> Result<f64, anyhow::Error>;
|
||||
}
|
||||
|
||||
pub struct Metrics {
|
||||
endpoint: Url,
|
||||
endpoint: Url,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
fn new(endpoint: impl Into<Url>) -> Self {
|
||||
Self {
|
||||
endpoint: endpoint.into(),
|
||||
}
|
||||
}
|
||||
fn new(endpoint: impl Into<Url>) -> Self {
|
||||
Self { endpoint: endpoint.into() }
|
||||
}
|
||||
|
||||
async fn fetch_metrics(
|
||||
endpoint: impl AsRef<str>,
|
||||
) -> Result<HashMap<String, f64>, anyhow::Error> {
|
||||
let response = reqwest::get(endpoint.as_ref()).await?;
|
||||
Ok(prom_metrics_parser::parse(&response.text().await?)?)
|
||||
}
|
||||
async fn fetch_metrics(
|
||||
endpoint: impl AsRef<str>,
|
||||
) -> Result<HashMap<String, f64>, anyhow::Error> {
|
||||
let response = reqwest::get(endpoint.as_ref()).await?;
|
||||
Ok(prom_metrics_parser::parse(&response.text().await?)?)
|
||||
}
|
||||
|
||||
fn get_metric(
|
||||
metrics_map: HashMap<String, f64>,
|
||||
metric_name: &str,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let treat_not_found_as_zero = true;
|
||||
if let Some(val) = metrics_map.get(metric_name) {
|
||||
Ok(*val)
|
||||
} else if treat_not_found_as_zero {
|
||||
Ok(0_f64)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("MetricNotFound: {metric_name}"))
|
||||
}
|
||||
}
|
||||
fn get_metric(
|
||||
metrics_map: HashMap<String, f64>,
|
||||
metric_name: &str,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let treat_not_found_as_zero = true;
|
||||
if let Some(val) = metrics_map.get(metric_name) {
|
||||
Ok(*val)
|
||||
} else if treat_not_found_as_zero {
|
||||
Ok(0_f64)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("MetricNotFound: {metric_name}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MetricsHelper for Metrics {
|
||||
async fn metric(&self, metric_name: &str) -> Result<f64, anyhow::Error> {
|
||||
let metrics_map = Metrics::fetch_metrics(self.endpoint.as_str()).await?;
|
||||
Metrics::get_metric(metrics_map, metric_name)
|
||||
}
|
||||
async fn metric(&self, metric_name: &str) -> Result<f64, anyhow::Error> {
|
||||
let metrics_map = Metrics::fetch_metrics(self.endpoint.as_str()).await?;
|
||||
Metrics::get_metric(metrics_map, metric_name)
|
||||
}
|
||||
|
||||
async fn metric_with_url(
|
||||
metric_name: impl AsRef<str> + Send,
|
||||
endpoint: impl Into<Url> + Send,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let metrics_map = Metrics::fetch_metrics(endpoint.into()).await?;
|
||||
Metrics::get_metric(metrics_map, metric_name.as_ref())
|
||||
}
|
||||
async fn metric_with_url(
|
||||
metric_name: impl AsRef<str> + Send,
|
||||
endpoint: impl Into<Url> + Send,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let metrics_map = Metrics::fetch_metrics(endpoint.into()).await?;
|
||||
Metrics::get_metric(metrics_map, metric_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -6,29 +6,29 @@ use tracing::trace;
|
||||
use crate::network::node::NetworkNode;
|
||||
|
||||
pub(crate) async fn verify_nodes(nodes: &[&NetworkNode]) -> Result<(), anyhow::Error> {
|
||||
timeout(Duration::from_secs(90), check_nodes(nodes))
|
||||
.await
|
||||
.map_err(|_| anyhow::anyhow!("one or more nodes are not ready!"))
|
||||
timeout(Duration::from_secs(90), check_nodes(nodes))
|
||||
.await
|
||||
.map_err(|_| anyhow::anyhow!("one or more nodes are not ready!"))
|
||||
}
|
||||
|
||||
// TODO: we should inject in someway the logic to make the request
|
||||
// in order to allow us to `mock` and easily test this.
|
||||
// maybe moved to the provider with a NodeStatus, and some helpers like wait_running, wait_ready, etc... ? to be discussed
|
||||
async fn check_nodes(nodes: &[&NetworkNode]) {
|
||||
loop {
|
||||
let tasks: Vec<_> = nodes
|
||||
.iter()
|
||||
.map(|node| {
|
||||
trace!("🔎 checking node: {} ", node.name);
|
||||
reqwest::get(node.prometheus_uri.clone())
|
||||
})
|
||||
.collect();
|
||||
loop {
|
||||
let tasks: Vec<_> = nodes
|
||||
.iter()
|
||||
.map(|node| {
|
||||
trace!("🔎 checking node: {} ", node.name);
|
||||
reqwest::get(node.prometheus_uri.clone())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let all_ready = futures::future::try_join_all(tasks).await;
|
||||
if all_ready.is_ok() {
|
||||
return;
|
||||
}
|
||||
let all_ready = futures::future::try_join_all(tasks).await;
|
||||
if all_ready.is_ok() {
|
||||
return;
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||
}
|
||||
}
|
||||
|
||||
+242
-264
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use configuration::{GlobalSettings, HrmpChannelConfig, NetworkConfig};
|
||||
@@ -20,311 +20,289 @@ use self::{node::NodeSpec, relaychain::RelaychainSpec, teyrchain::TeyrchainSpec}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkSpec {
|
||||
/// Relaychain configuration.
|
||||
pub(crate) relaychain: RelaychainSpec,
|
||||
/// Relaychain configuration.
|
||||
pub(crate) relaychain: RelaychainSpec,
|
||||
|
||||
/// Parachains configurations.
|
||||
pub(crate) parachains: Vec<TeyrchainSpec>,
|
||||
/// Parachains configurations.
|
||||
pub(crate) parachains: Vec<TeyrchainSpec>,
|
||||
|
||||
/// HRMP channels configurations.
|
||||
pub(crate) hrmp_channels: Vec<HrmpChannelConfig>,
|
||||
/// HRMP channels configurations.
|
||||
pub(crate) hrmp_channels: Vec<HrmpChannelConfig>,
|
||||
|
||||
/// Global settings
|
||||
pub(crate) global_settings: GlobalSettings,
|
||||
/// Global settings
|
||||
pub(crate) global_settings: GlobalSettings,
|
||||
}
|
||||
|
||||
impl NetworkSpec {
|
||||
pub async fn from_config(
|
||||
network_config: &NetworkConfig,
|
||||
) -> Result<NetworkSpec, OrchestratorError> {
|
||||
let mut errs = vec![];
|
||||
let relaychain = RelaychainSpec::from_config(network_config.relaychain())?;
|
||||
let mut parachains = vec![];
|
||||
pub async fn from_config(
|
||||
network_config: &NetworkConfig,
|
||||
) -> Result<NetworkSpec, OrchestratorError> {
|
||||
let mut errs = vec![];
|
||||
let relaychain = RelaychainSpec::from_config(network_config.relaychain())?;
|
||||
let mut parachains = vec![];
|
||||
|
||||
// TODO: move to `fold` or map+fold
|
||||
for para_config in network_config.parachains() {
|
||||
match TeyrchainSpec::from_config(para_config, relaychain.chain.clone()) {
|
||||
Ok(para) => parachains.push(para),
|
||||
Err(err) => errs.push(err),
|
||||
}
|
||||
}
|
||||
// TODO: move to `fold` or map+fold
|
||||
for para_config in network_config.parachains() {
|
||||
match TeyrchainSpec::from_config(para_config, relaychain.chain.clone()) {
|
||||
Ok(para) => parachains.push(para),
|
||||
Err(err) => errs.push(err),
|
||||
}
|
||||
}
|
||||
|
||||
if errs.is_empty() {
|
||||
Ok(NetworkSpec {
|
||||
relaychain,
|
||||
parachains,
|
||||
hrmp_channels: network_config
|
||||
.hrmp_channels()
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
global_settings: network_config.global_settings().clone(),
|
||||
})
|
||||
} else {
|
||||
let errs_str = errs
|
||||
.into_iter()
|
||||
.map(|e| e.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
Err(OrchestratorError::InvalidConfig(errs_str))
|
||||
}
|
||||
}
|
||||
if errs.is_empty() {
|
||||
Ok(NetworkSpec {
|
||||
relaychain,
|
||||
parachains,
|
||||
hrmp_channels: network_config.hrmp_channels().into_iter().cloned().collect(),
|
||||
global_settings: network_config.global_settings().clone(),
|
||||
})
|
||||
} else {
|
||||
let errs_str =
|
||||
errs.into_iter().map(|e| e.to_string()).collect::<Vec<String>>().join("\n");
|
||||
Err(OrchestratorError::InvalidConfig(errs_str))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn populate_nodes_available_args(
|
||||
&mut self,
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
) -> Result<(), OrchestratorError> {
|
||||
let network_nodes = self.collect_network_nodes();
|
||||
pub async fn populate_nodes_available_args(
|
||||
&mut self,
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
) -> Result<(), OrchestratorError> {
|
||||
let network_nodes = self.collect_network_nodes();
|
||||
|
||||
let mut image_command_to_nodes_mapping =
|
||||
Self::create_image_command_to_nodes_mapping(network_nodes);
|
||||
let mut image_command_to_nodes_mapping =
|
||||
Self::create_image_command_to_nodes_mapping(network_nodes);
|
||||
|
||||
let available_args_outputs =
|
||||
Self::retrieve_all_nodes_available_args_output(ns, &image_command_to_nodes_mapping)
|
||||
.await?;
|
||||
let available_args_outputs =
|
||||
Self::retrieve_all_nodes_available_args_output(ns, &image_command_to_nodes_mapping)
|
||||
.await?;
|
||||
|
||||
Self::update_nodes_available_args_output(
|
||||
&mut image_command_to_nodes_mapping,
|
||||
available_args_outputs,
|
||||
);
|
||||
Self::update_nodes_available_args_output(
|
||||
&mut image_command_to_nodes_mapping,
|
||||
available_args_outputs,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//
|
||||
pub async fn node_available_args_output(
|
||||
&self,
|
||||
node_spec: &NodeSpec,
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
) -> Result<String, ProviderError> {
|
||||
// try to find a node that use the same combination of image/cmd
|
||||
let cmp_fn = |ad_hoc: &&NodeSpec| -> bool {
|
||||
ad_hoc.image == node_spec.image && ad_hoc.command == node_spec.command
|
||||
};
|
||||
//
|
||||
pub async fn node_available_args_output(
|
||||
&self,
|
||||
node_spec: &NodeSpec,
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
) -> Result<String, ProviderError> {
|
||||
// try to find a node that use the same combination of image/cmd
|
||||
let cmp_fn = |ad_hoc: &&NodeSpec| -> bool {
|
||||
ad_hoc.image == node_spec.image && ad_hoc.command == node_spec.command
|
||||
};
|
||||
|
||||
// check if we already had computed the args output for this cmd/[image]
|
||||
let node = self.relaychain.nodes.iter().find(cmp_fn);
|
||||
let node = if let Some(node) = node {
|
||||
Some(node)
|
||||
} else {
|
||||
let node = self
|
||||
.parachains
|
||||
.iter()
|
||||
.find_map(|para| para.collators.iter().find(cmp_fn));
|
||||
// check if we already had computed the args output for this cmd/[image]
|
||||
let node = self.relaychain.nodes.iter().find(cmp_fn);
|
||||
let node = if let Some(node) = node {
|
||||
Some(node)
|
||||
} else {
|
||||
let node = self.parachains.iter().find_map(|para| para.collators.iter().find(cmp_fn));
|
||||
|
||||
node
|
||||
};
|
||||
node
|
||||
};
|
||||
|
||||
let output = if let Some(node) = node {
|
||||
node.available_args_output.clone().expect(&format!(
|
||||
"args_output should be set for running nodes {THIS_IS_A_BUG}"
|
||||
))
|
||||
} else {
|
||||
// we need to compute the args output
|
||||
let image = node_spec
|
||||
.image
|
||||
.as_ref()
|
||||
.map(|image| image.as_str().to_string());
|
||||
let command = node_spec.command.as_str().to_string();
|
||||
let output = if let Some(node) = node {
|
||||
node.available_args_output
|
||||
.clone()
|
||||
.expect(&format!("args_output should be set for running nodes {THIS_IS_A_BUG}"))
|
||||
} else {
|
||||
// we need to compute the args output
|
||||
let image = node_spec.image.as_ref().map(|image| image.as_str().to_string());
|
||||
let command = node_spec.command.as_str().to_string();
|
||||
|
||||
ns.get_node_available_args((command, image)).await?
|
||||
};
|
||||
ns.get_node_available_args((command, image)).await?
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn relaychain(&self) -> &RelaychainSpec {
|
||||
&self.relaychain
|
||||
}
|
||||
pub fn relaychain(&self) -> &RelaychainSpec {
|
||||
&self.relaychain
|
||||
}
|
||||
|
||||
pub fn relaychain_mut(&mut self) -> &mut RelaychainSpec {
|
||||
&mut self.relaychain
|
||||
}
|
||||
pub fn relaychain_mut(&mut self) -> &mut RelaychainSpec {
|
||||
&mut self.relaychain
|
||||
}
|
||||
|
||||
pub fn parachains_iter(&self) -> impl Iterator<Item = &TeyrchainSpec> {
|
||||
self.parachains.iter()
|
||||
}
|
||||
pub fn parachains_iter(&self) -> impl Iterator<Item = &TeyrchainSpec> {
|
||||
self.parachains.iter()
|
||||
}
|
||||
|
||||
pub fn parachains_iter_mut(&mut self) -> impl Iterator<Item = &mut TeyrchainSpec> {
|
||||
self.parachains.iter_mut()
|
||||
}
|
||||
pub fn parachains_iter_mut(&mut self) -> impl Iterator<Item = &mut TeyrchainSpec> {
|
||||
self.parachains.iter_mut()
|
||||
}
|
||||
|
||||
pub fn set_global_settings(&mut self, global_settings: GlobalSettings) {
|
||||
self.global_settings = global_settings;
|
||||
}
|
||||
pub fn set_global_settings(&mut self, global_settings: GlobalSettings) {
|
||||
self.global_settings = global_settings;
|
||||
}
|
||||
|
||||
pub async fn build_parachain_artifacts<'a, T: FileSystem>(
|
||||
&mut self,
|
||||
ns: DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
relaychain_id: &str,
|
||||
base_dir_exists: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
for para in self.parachains.iter_mut() {
|
||||
let chain_spec_raw_path = para.build_chain_spec(relaychain_id, &ns, scoped_fs).await?;
|
||||
pub async fn build_parachain_artifacts<'a, T: FileSystem>(
|
||||
&mut self,
|
||||
ns: DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
relaychain_id: &str,
|
||||
base_dir_exists: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
for para in self.parachains.iter_mut() {
|
||||
let chain_spec_raw_path = para.build_chain_spec(relaychain_id, &ns, scoped_fs).await?;
|
||||
|
||||
trace!("creating dirs for {}", ¶.unique_id);
|
||||
if base_dir_exists {
|
||||
scoped_fs.create_dir_all(¶.unique_id).await?;
|
||||
} else {
|
||||
scoped_fs.create_dir(¶.unique_id).await?;
|
||||
};
|
||||
trace!("created dirs for {}", ¶.unique_id);
|
||||
trace!("creating dirs for {}", ¶.unique_id);
|
||||
if base_dir_exists {
|
||||
scoped_fs.create_dir_all(¶.unique_id).await?;
|
||||
} else {
|
||||
scoped_fs.create_dir(¶.unique_id).await?;
|
||||
};
|
||||
trace!("created dirs for {}", ¶.unique_id);
|
||||
|
||||
// create wasm/state
|
||||
para.genesis_state
|
||||
.build(
|
||||
chain_spec_raw_path.clone(),
|
||||
format!("{}/genesis-state", para.unique_id),
|
||||
&ns,
|
||||
scoped_fs,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
debug!("parachain genesis state built!");
|
||||
para.genesis_wasm
|
||||
.build(
|
||||
chain_spec_raw_path,
|
||||
format!("{}/genesis-wasm", para.unique_id),
|
||||
&ns,
|
||||
scoped_fs,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
debug!("parachain genesis wasm built!");
|
||||
}
|
||||
// create wasm/state
|
||||
para.genesis_state
|
||||
.build(
|
||||
chain_spec_raw_path.clone(),
|
||||
format!("{}/genesis-state", para.unique_id),
|
||||
&ns,
|
||||
scoped_fs,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
debug!("parachain genesis state built!");
|
||||
para.genesis_wasm
|
||||
.build(
|
||||
chain_spec_raw_path,
|
||||
format!("{}/genesis-wasm", para.unique_id),
|
||||
&ns,
|
||||
scoped_fs,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
debug!("parachain genesis wasm built!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// collect mutable references to all nodes from relaychain and parachains
|
||||
fn collect_network_nodes(&mut self) -> Vec<&mut NodeSpec> {
|
||||
vec![
|
||||
self.relaychain.nodes.iter_mut().collect::<Vec<_>>(),
|
||||
self.parachains
|
||||
.iter_mut()
|
||||
.flat_map(|para| para.collators.iter_mut())
|
||||
.collect(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
// collect mutable references to all nodes from relaychain and parachains
|
||||
fn collect_network_nodes(&mut self) -> Vec<&mut NodeSpec> {
|
||||
vec![
|
||||
self.relaychain.nodes.iter_mut().collect::<Vec<_>>(),
|
||||
self.parachains.iter_mut().flat_map(|para| para.collators.iter_mut()).collect(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
// initialize the mapping of all possible node image/commands to corresponding nodes
|
||||
fn create_image_command_to_nodes_mapping(
|
||||
network_nodes: Vec<&mut NodeSpec>,
|
||||
) -> HashMap<(Option<String>, String), Vec<&mut NodeSpec>> {
|
||||
network_nodes.into_iter().fold(
|
||||
HashMap::new(),
|
||||
|mut acc: HashMap<(Option<String>, String), Vec<&mut node::NodeSpec>>, node| {
|
||||
// build mapping key using image and command if image is present or command only
|
||||
let key = node
|
||||
.image
|
||||
.as_ref()
|
||||
.map(|image| {
|
||||
(
|
||||
Some(image.as_str().to_string()),
|
||||
node.command.as_str().to_string(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| (None, node.command.as_str().to_string()));
|
||||
// initialize the mapping of all possible node image/commands to corresponding nodes
|
||||
fn create_image_command_to_nodes_mapping(
|
||||
network_nodes: Vec<&mut NodeSpec>,
|
||||
) -> HashMap<(Option<String>, String), Vec<&mut NodeSpec>> {
|
||||
network_nodes.into_iter().fold(
|
||||
HashMap::new(),
|
||||
|mut acc: HashMap<(Option<String>, String), Vec<&mut node::NodeSpec>>, node| {
|
||||
// build mapping key using image and command if image is present or command only
|
||||
let key = node
|
||||
.image
|
||||
.as_ref()
|
||||
.map(|image| {
|
||||
(Some(image.as_str().to_string()), node.command.as_str().to_string())
|
||||
})
|
||||
.unwrap_or_else(|| (None, node.command.as_str().to_string()));
|
||||
|
||||
// append the node to the vector of nodes for this image/command tuple
|
||||
if let Entry::Vacant(entry) = acc.entry(key.clone()) {
|
||||
entry.insert(vec![node]);
|
||||
} else {
|
||||
acc.get_mut(&key).unwrap().push(node);
|
||||
}
|
||||
// append the node to the vector of nodes for this image/command tuple
|
||||
if let Entry::Vacant(entry) = acc.entry(key.clone()) {
|
||||
entry.insert(vec![node]);
|
||||
} else {
|
||||
acc.get_mut(&key).unwrap().push(node);
|
||||
}
|
||||
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn retrieve_all_nodes_available_args_output(
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
image_command_to_nodes_mapping: &HashMap<(Option<String>, String), Vec<&mut NodeSpec>>,
|
||||
) -> Result<Vec<(Option<String>, String, String)>, OrchestratorError> {
|
||||
try_join_all(
|
||||
image_command_to_nodes_mapping
|
||||
.keys()
|
||||
.map(|(image, command)| {
|
||||
let ns = ns.clone();
|
||||
let image = image.clone();
|
||||
let command = command.clone();
|
||||
async move {
|
||||
// get node available args output from image/command
|
||||
let available_args = ns
|
||||
.get_node_available_args((command.clone(), image.clone()))
|
||||
.await?;
|
||||
debug!(
|
||||
"retrieved available args for image: {:?}, command: {}",
|
||||
image, command
|
||||
);
|
||||
async fn retrieve_all_nodes_available_args_output(
|
||||
ns: Arc<dyn ProviderNamespace + Send + Sync>,
|
||||
image_command_to_nodes_mapping: &HashMap<(Option<String>, String), Vec<&mut NodeSpec>>,
|
||||
) -> Result<Vec<(Option<String>, String, String)>, OrchestratorError> {
|
||||
try_join_all(
|
||||
image_command_to_nodes_mapping
|
||||
.keys()
|
||||
.map(|(image, command)| {
|
||||
let ns = ns.clone();
|
||||
let image = image.clone();
|
||||
let command = command.clone();
|
||||
async move {
|
||||
// get node available args output from image/command
|
||||
let available_args =
|
||||
ns.get_node_available_args((command.clone(), image.clone())).await?;
|
||||
debug!(
|
||||
"retrieved available args for image: {:?}, command: {}",
|
||||
image, command
|
||||
);
|
||||
|
||||
// map the result to include image and command
|
||||
Ok::<_, OrchestratorError>((image, command, available_args))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
// map the result to include image and command
|
||||
Ok::<_, OrchestratorError>((image, command, available_args))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn update_nodes_available_args_output(
|
||||
image_command_to_nodes_mapping: &mut HashMap<(Option<String>, String), Vec<&mut NodeSpec>>,
|
||||
available_args_outputs: Vec<(Option<String>, String, String)>,
|
||||
) {
|
||||
for (image, command, available_args_output) in available_args_outputs {
|
||||
let nodes = image_command_to_nodes_mapping
|
||||
.get_mut(&(image, command))
|
||||
.expect(&format!(
|
||||
"node image/command key should exist {THIS_IS_A_BUG}"
|
||||
));
|
||||
fn update_nodes_available_args_output(
|
||||
image_command_to_nodes_mapping: &mut HashMap<(Option<String>, String), Vec<&mut NodeSpec>>,
|
||||
available_args_outputs: Vec<(Option<String>, String, String)>,
|
||||
) {
|
||||
for (image, command, available_args_output) in available_args_outputs {
|
||||
let nodes = image_command_to_nodes_mapping
|
||||
.get_mut(&(image, command))
|
||||
.expect(&format!("node image/command key should exist {THIS_IS_A_BUG}"));
|
||||
|
||||
for node in nodes {
|
||||
node.available_args_output = Some(available_args_output.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
for node in nodes {
|
||||
node.available_args_output = Some(available_args_output.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn small_network_config_get_spec() {
|
||||
use configuration::NetworkConfigBuilder;
|
||||
#[tokio::test]
|
||||
async fn small_network_config_get_spec() {
|
||||
use configuration::NetworkConfigBuilder;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
let config = NetworkConfigBuilder::new()
|
||||
.with_relaychain(|r| {
|
||||
r.with_chain("rococo-local")
|
||||
.with_default_command("polkadot")
|
||||
.with_validator(|node| node.with_name("alice"))
|
||||
.with_fullnode(|node| node.with_name("bob").with_command("polkadot1"))
|
||||
})
|
||||
.with_parachain(|p| {
|
||||
p.with_id(100)
|
||||
.with_default_command("adder-collator")
|
||||
.with_collator(|c| c.with_name("collator1"))
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
let config = NetworkConfigBuilder::new()
|
||||
.with_relaychain(|r| {
|
||||
r.with_chain("rococo-local")
|
||||
.with_default_command("polkadot")
|
||||
.with_validator(|node| node.with_name("alice"))
|
||||
.with_fullnode(|node| node.with_name("bob").with_command("polkadot1"))
|
||||
})
|
||||
.with_parachain(|p| {
|
||||
p.with_id(100)
|
||||
.with_default_command("adder-collator")
|
||||
.with_collator(|c| c.with_name("collator1"))
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let network_spec = NetworkSpec::from_config(&config).await.unwrap();
|
||||
let alice = network_spec.relaychain.nodes.first().unwrap();
|
||||
let bob = network_spec.relaychain.nodes.get(1).unwrap();
|
||||
assert_eq!(alice.command.as_str(), "polkadot");
|
||||
assert_eq!(bob.command.as_str(), "polkadot1");
|
||||
assert!(alice.is_validator);
|
||||
assert!(!bob.is_validator);
|
||||
let network_spec = NetworkSpec::from_config(&config).await.unwrap();
|
||||
let alice = network_spec.relaychain.nodes.first().unwrap();
|
||||
let bob = network_spec.relaychain.nodes.get(1).unwrap();
|
||||
assert_eq!(alice.command.as_str(), "polkadot");
|
||||
assert_eq!(bob.command.as_str(), "polkadot1");
|
||||
assert!(alice.is_validator);
|
||||
assert!(!bob.is_validator);
|
||||
|
||||
// paras
|
||||
assert_eq!(network_spec.parachains.len(), 1);
|
||||
let para_100 = network_spec.parachains.first().unwrap();
|
||||
assert_eq!(para_100.id, 100);
|
||||
}
|
||||
// paras
|
||||
assert_eq!(network_spec.parachains.len(), 1);
|
||||
let para_100 = network_spec.parachains.first().unwrap();
|
||||
assert_eq!(para_100.id, 100);
|
||||
}
|
||||
}
|
||||
|
||||
+259
-283
@@ -1,9 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use configuration::shared::{
|
||||
node::{EnvVar, NodeConfig},
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Command, Image},
|
||||
node::{EnvVar, NodeConfig},
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Command, Image},
|
||||
};
|
||||
use multiaddr::Multiaddr;
|
||||
use provider::types::Port;
|
||||
@@ -11,39 +11,39 @@ use serde::{Deserialize, Serialize};
|
||||
use support::constants::THIS_IS_A_BUG;
|
||||
|
||||
use crate::{
|
||||
errors::OrchestratorError,
|
||||
generators,
|
||||
network::AddNodeOptions,
|
||||
shared::{
|
||||
macros,
|
||||
types::{ChainDefaultContext, NodeAccount, NodeAccounts, ParkedPort},
|
||||
},
|
||||
AddCollatorOptions,
|
||||
errors::OrchestratorError,
|
||||
generators,
|
||||
network::AddNodeOptions,
|
||||
shared::{
|
||||
macros,
|
||||
types::{ChainDefaultContext, NodeAccount, NodeAccounts, ParkedPort},
|
||||
},
|
||||
AddCollatorOptions,
|
||||
};
|
||||
|
||||
macros::create_add_options!(AddNodeSpecOpts {
|
||||
override_eth_key: Option<String>
|
||||
override_eth_key: Option<String>
|
||||
});
|
||||
|
||||
macro_rules! impl_from_for_add_node_opts {
|
||||
($struct:ident) => {
|
||||
impl From<$struct> for AddNodeSpecOpts {
|
||||
fn from(value: $struct) -> Self {
|
||||
Self {
|
||||
image: value.image,
|
||||
command: value.command,
|
||||
subcommand: value.subcommand,
|
||||
args: value.args,
|
||||
env: value.env,
|
||||
is_validator: value.is_validator,
|
||||
rpc_port: value.rpc_port,
|
||||
prometheus_port: value.prometheus_port,
|
||||
p2p_port: value.p2p_port,
|
||||
override_eth_key: value.override_eth_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($struct:ident) => {
|
||||
impl From<$struct> for AddNodeSpecOpts {
|
||||
fn from(value: $struct) -> Self {
|
||||
Self {
|
||||
image: value.image,
|
||||
command: value.command,
|
||||
subcommand: value.subcommand,
|
||||
args: value.args,
|
||||
env: value.env,
|
||||
is_validator: value.is_validator,
|
||||
rpc_port: value.rpc_port,
|
||||
prometheus_port: value.prometheus_port,
|
||||
p2p_port: value.p2p_port,
|
||||
override_eth_key: value.override_eth_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_for_add_node_opts!(AddNodeOptions);
|
||||
@@ -52,305 +52,281 @@ impl_from_for_add_node_opts!(AddCollatorOptions);
|
||||
/// A node configuration, with fine-grained configuration options.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct NodeSpec {
|
||||
// Node name (should be unique or an index will be appended).
|
||||
pub(crate) name: String,
|
||||
// Node name (should be unique or an index will be appended).
|
||||
pub(crate) name: String,
|
||||
|
||||
/// Node key, used for compute the p2p identity.
|
||||
pub(crate) key: String,
|
||||
/// Node key, used for compute the p2p identity.
|
||||
pub(crate) key: String,
|
||||
|
||||
// libp2p local identity
|
||||
pub(crate) peer_id: String,
|
||||
// libp2p local identity
|
||||
pub(crate) peer_id: String,
|
||||
|
||||
/// Accounts to be injected in the keystore.
|
||||
pub(crate) accounts: NodeAccounts,
|
||||
/// Accounts to be injected in the keystore.
|
||||
pub(crate) accounts: NodeAccounts,
|
||||
|
||||
/// Image to run (only podman/k8s). Override the default.
|
||||
pub(crate) image: Option<Image>,
|
||||
/// Image to run (only podman/k8s). Override the default.
|
||||
pub(crate) image: Option<Image>,
|
||||
|
||||
/// Command to run the node. Override the default.
|
||||
pub(crate) command: Command,
|
||||
/// Command to run the node. Override the default.
|
||||
pub(crate) command: Command,
|
||||
|
||||
/// Optional subcommand for the node.
|
||||
pub(crate) subcommand: Option<Command>,
|
||||
/// Optional subcommand for the node.
|
||||
pub(crate) subcommand: Option<Command>,
|
||||
|
||||
/// Arguments to use for node. Appended to default.
|
||||
pub(crate) args: Vec<Arg>,
|
||||
/// Arguments to use for node. Appended to default.
|
||||
pub(crate) args: Vec<Arg>,
|
||||
|
||||
// The help command output containing the available arguments.
|
||||
pub(crate) available_args_output: Option<String>,
|
||||
// The help command output containing the available arguments.
|
||||
pub(crate) available_args_output: Option<String>,
|
||||
|
||||
/// Wether the node is a validator.
|
||||
pub(crate) is_validator: bool,
|
||||
/// Wether the node is a validator.
|
||||
pub(crate) is_validator: bool,
|
||||
|
||||
/// Whether the node keys must be added to invulnerables.
|
||||
pub(crate) is_invulnerable: bool,
|
||||
/// Whether the node keys must be added to invulnerables.
|
||||
pub(crate) is_invulnerable: bool,
|
||||
|
||||
/// Whether the node is a bootnode.
|
||||
pub(crate) is_bootnode: bool,
|
||||
/// Whether the node is a bootnode.
|
||||
pub(crate) is_bootnode: bool,
|
||||
|
||||
/// Node initial balance present in genesis.
|
||||
pub(crate) initial_balance: u128,
|
||||
/// Node initial balance present in genesis.
|
||||
pub(crate) initial_balance: u128,
|
||||
|
||||
/// Environment variables to set (inside pod for podman/k8s, inside shell for native).
|
||||
pub(crate) env: Vec<EnvVar>,
|
||||
/// Environment variables to set (inside pod for podman/k8s, inside shell for native).
|
||||
pub(crate) env: Vec<EnvVar>,
|
||||
|
||||
/// List of node's bootnodes addresses to use. Appended to default.
|
||||
pub(crate) bootnodes_addresses: Vec<Multiaddr>,
|
||||
/// List of node's bootnodes addresses to use. Appended to default.
|
||||
pub(crate) bootnodes_addresses: Vec<Multiaddr>,
|
||||
|
||||
/// Default resources. Override the default.
|
||||
pub(crate) resources: Option<Resources>,
|
||||
/// Default resources. Override the default.
|
||||
pub(crate) resources: Option<Resources>,
|
||||
|
||||
/// Websocket port to use.
|
||||
pub(crate) ws_port: ParkedPort,
|
||||
/// Websocket port to use.
|
||||
pub(crate) ws_port: ParkedPort,
|
||||
|
||||
/// RPC port to use.
|
||||
pub(crate) rpc_port: ParkedPort,
|
||||
/// RPC port to use.
|
||||
pub(crate) rpc_port: ParkedPort,
|
||||
|
||||
/// Prometheus port to use.
|
||||
pub(crate) prometheus_port: ParkedPort,
|
||||
/// Prometheus port to use.
|
||||
pub(crate) prometheus_port: ParkedPort,
|
||||
|
||||
/// P2P port to use.
|
||||
pub(crate) p2p_port: ParkedPort,
|
||||
/// P2P port to use.
|
||||
pub(crate) p2p_port: ParkedPort,
|
||||
|
||||
/// libp2p cert hash to use with `webrtc` transport.
|
||||
pub(crate) p2p_cert_hash: Option<String>,
|
||||
/// libp2p cert hash to use with `webrtc` transport.
|
||||
pub(crate) p2p_cert_hash: Option<String>,
|
||||
|
||||
/// Database snapshot. Override the default.
|
||||
pub(crate) db_snapshot: Option<AssetLocation>,
|
||||
/// Database snapshot. Override the default.
|
||||
pub(crate) db_snapshot: Option<AssetLocation>,
|
||||
|
||||
/// P2P port to use by full node if this is the case
|
||||
pub(crate) full_node_p2p_port: Option<ParkedPort>,
|
||||
/// Prometheus port to use by full node if this is the case
|
||||
pub(crate) full_node_prometheus_port: Option<ParkedPort>,
|
||||
/// P2P port to use by full node if this is the case
|
||||
pub(crate) full_node_p2p_port: Option<ParkedPort>,
|
||||
/// Prometheus port to use by full node if this is the case
|
||||
pub(crate) full_node_prometheus_port: Option<ParkedPort>,
|
||||
|
||||
/// Optionally specify a log path for the node
|
||||
pub(crate) node_log_path: Option<PathBuf>,
|
||||
/// Optionally specify a log path for the node
|
||||
pub(crate) node_log_path: Option<PathBuf>,
|
||||
|
||||
/// Optionally specify a keystore path for the node
|
||||
pub(crate) keystore_path: Option<PathBuf>,
|
||||
/// Optionally specify a keystore path for the node
|
||||
pub(crate) keystore_path: Option<PathBuf>,
|
||||
|
||||
/// Keystore key types to generate.
|
||||
/// Supports short form (e.g., "audi") using predefined schemas,
|
||||
/// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec).
|
||||
pub(crate) keystore_key_types: Vec<String>,
|
||||
/// Keystore key types to generate.
|
||||
/// Supports short form (e.g., "audi") using predefined schemas,
|
||||
/// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec).
|
||||
pub(crate) keystore_key_types: Vec<String>,
|
||||
}
|
||||
|
||||
impl NodeSpec {
|
||||
pub fn from_config(
|
||||
node_config: &NodeConfig,
|
||||
chain_context: &ChainDefaultContext,
|
||||
full_node_present: bool,
|
||||
evm_based: bool,
|
||||
) -> Result<Self, OrchestratorError> {
|
||||
// Check first if the image is set at node level, then try with the default
|
||||
let image = node_config.image().or(chain_context.default_image).cloned();
|
||||
pub fn from_config(
|
||||
node_config: &NodeConfig,
|
||||
chain_context: &ChainDefaultContext,
|
||||
full_node_present: bool,
|
||||
evm_based: bool,
|
||||
) -> Result<Self, OrchestratorError> {
|
||||
// Check first if the image is set at node level, then try with the default
|
||||
let image = node_config.image().or(chain_context.default_image).cloned();
|
||||
|
||||
// Check first if the command is set at node level, then try with the default
|
||||
let command = if let Some(cmd) = node_config.command() {
|
||||
cmd.clone()
|
||||
} else if let Some(cmd) = chain_context.default_command {
|
||||
cmd.clone()
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidNodeConfig(
|
||||
node_config.name().into(),
|
||||
"command".to_string(),
|
||||
));
|
||||
};
|
||||
// Check first if the command is set at node level, then try with the default
|
||||
let command = if let Some(cmd) = node_config.command() {
|
||||
cmd.clone()
|
||||
} else if let Some(cmd) = chain_context.default_command {
|
||||
cmd.clone()
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidNodeConfig(
|
||||
node_config.name().into(),
|
||||
"command".to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let subcommand = node_config.subcommand().cloned();
|
||||
let subcommand = node_config.subcommand().cloned();
|
||||
|
||||
// If `args` is set at `node` level use them
|
||||
// otherwise use the default_args (can be empty).
|
||||
let args: Vec<Arg> = if node_config.args().is_empty() {
|
||||
chain_context
|
||||
.default_args
|
||||
.iter()
|
||||
.map(|x| x.to_owned().clone())
|
||||
.collect()
|
||||
} else {
|
||||
node_config.args().into_iter().cloned().collect()
|
||||
};
|
||||
// If `args` is set at `node` level use them
|
||||
// otherwise use the default_args (can be empty).
|
||||
let args: Vec<Arg> = if node_config.args().is_empty() {
|
||||
chain_context.default_args.iter().map(|x| x.to_owned().clone()).collect()
|
||||
} else {
|
||||
node_config.args().into_iter().cloned().collect()
|
||||
};
|
||||
|
||||
let (key, peer_id) = generators::generate_node_identity(node_config.name())?;
|
||||
let (key, peer_id) = generators::generate_node_identity(node_config.name())?;
|
||||
|
||||
let mut name = node_config.name().to_string();
|
||||
let seed = format!("//{}{name}", name.remove(0).to_uppercase());
|
||||
let accounts = generators::generate_node_keys(&seed)?;
|
||||
let mut accounts = NodeAccounts { seed, accounts };
|
||||
let mut name = node_config.name().to_string();
|
||||
let seed = format!("//{}{name}", name.remove(0).to_uppercase());
|
||||
let accounts = generators::generate_node_keys(&seed)?;
|
||||
let mut accounts = NodeAccounts { seed, accounts };
|
||||
|
||||
if evm_based {
|
||||
if let Some(session_key) = node_config.override_eth_key() {
|
||||
accounts
|
||||
.accounts
|
||||
.insert("eth".into(), NodeAccount::new(session_key, session_key));
|
||||
}
|
||||
}
|
||||
if evm_based {
|
||||
if let Some(session_key) = node_config.override_eth_key() {
|
||||
accounts.accounts.insert("eth".into(), NodeAccount::new(session_key, session_key));
|
||||
}
|
||||
}
|
||||
|
||||
let db_snapshot = match (node_config.db_snapshot(), chain_context.default_db_snapshot) {
|
||||
(Some(db_snapshot), _) => Some(db_snapshot),
|
||||
(None, Some(db_snapshot)) => Some(db_snapshot),
|
||||
_ => None,
|
||||
};
|
||||
let db_snapshot = match (node_config.db_snapshot(), chain_context.default_db_snapshot) {
|
||||
(Some(db_snapshot), _) => Some(db_snapshot),
|
||||
(None, Some(db_snapshot)) => Some(db_snapshot),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present {
|
||||
(
|
||||
Some(generators::generate_node_port(None)?),
|
||||
Some(generators::generate_node_port(None)?),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present {
|
||||
(
|
||||
Some(generators::generate_node_port(None)?),
|
||||
Some(generators::generate_node_port(None)?),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name: node_config.name().to_string(),
|
||||
key,
|
||||
peer_id,
|
||||
image,
|
||||
command,
|
||||
subcommand,
|
||||
args,
|
||||
available_args_output: None,
|
||||
is_validator: node_config.is_validator(),
|
||||
is_invulnerable: node_config.is_invulnerable(),
|
||||
is_bootnode: node_config.is_bootnode(),
|
||||
initial_balance: node_config.initial_balance(),
|
||||
env: node_config.env().into_iter().cloned().collect(),
|
||||
bootnodes_addresses: node_config
|
||||
.bootnodes_addresses()
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
resources: node_config.resources().cloned(),
|
||||
p2p_cert_hash: node_config.p2p_cert_hash().map(str::to_string),
|
||||
db_snapshot: db_snapshot.cloned(),
|
||||
accounts,
|
||||
ws_port: generators::generate_node_port(node_config.ws_port())?,
|
||||
rpc_port: generators::generate_node_port(node_config.rpc_port())?,
|
||||
prometheus_port: generators::generate_node_port(node_config.prometheus_port())?,
|
||||
p2p_port: generators::generate_node_port(node_config.p2p_port())?,
|
||||
full_node_p2p_port,
|
||||
full_node_prometheus_port,
|
||||
node_log_path: node_config.node_log_path().cloned(),
|
||||
keystore_path: node_config.keystore_path().cloned(),
|
||||
keystore_key_types: node_config
|
||||
.keystore_key_types()
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
Ok(Self {
|
||||
name: node_config.name().to_string(),
|
||||
key,
|
||||
peer_id,
|
||||
image,
|
||||
command,
|
||||
subcommand,
|
||||
args,
|
||||
available_args_output: None,
|
||||
is_validator: node_config.is_validator(),
|
||||
is_invulnerable: node_config.is_invulnerable(),
|
||||
is_bootnode: node_config.is_bootnode(),
|
||||
initial_balance: node_config.initial_balance(),
|
||||
env: node_config.env().into_iter().cloned().collect(),
|
||||
bootnodes_addresses: node_config.bootnodes_addresses().into_iter().cloned().collect(),
|
||||
resources: node_config.resources().cloned(),
|
||||
p2p_cert_hash: node_config.p2p_cert_hash().map(str::to_string),
|
||||
db_snapshot: db_snapshot.cloned(),
|
||||
accounts,
|
||||
ws_port: generators::generate_node_port(node_config.ws_port())?,
|
||||
rpc_port: generators::generate_node_port(node_config.rpc_port())?,
|
||||
prometheus_port: generators::generate_node_port(node_config.prometheus_port())?,
|
||||
p2p_port: generators::generate_node_port(node_config.p2p_port())?,
|
||||
full_node_p2p_port,
|
||||
full_node_prometheus_port,
|
||||
node_log_path: node_config.node_log_path().cloned(),
|
||||
keystore_path: node_config.keystore_path().cloned(),
|
||||
keystore_key_types: node_config
|
||||
.keystore_key_types()
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_ad_hoc(
|
||||
name: impl Into<String>,
|
||||
options: AddNodeSpecOpts,
|
||||
chain_context: &ChainDefaultContext,
|
||||
full_node_present: bool,
|
||||
evm_based: bool,
|
||||
) -> Result<Self, OrchestratorError> {
|
||||
// Check first if the image is set at node level, then try with the default
|
||||
let image = if let Some(img) = options.image {
|
||||
Some(img.clone())
|
||||
} else {
|
||||
chain_context.default_image.cloned()
|
||||
};
|
||||
pub fn from_ad_hoc(
|
||||
name: impl Into<String>,
|
||||
options: AddNodeSpecOpts,
|
||||
chain_context: &ChainDefaultContext,
|
||||
full_node_present: bool,
|
||||
evm_based: bool,
|
||||
) -> Result<Self, OrchestratorError> {
|
||||
// Check first if the image is set at node level, then try with the default
|
||||
let image = if let Some(img) = options.image {
|
||||
Some(img.clone())
|
||||
} else {
|
||||
chain_context.default_image.cloned()
|
||||
};
|
||||
|
||||
let name = name.into();
|
||||
// Check first if the command is set at node level, then try with the default
|
||||
let command = if let Some(cmd) = options.command {
|
||||
cmd.clone()
|
||||
} else if let Some(cmd) = chain_context.default_command {
|
||||
cmd.clone()
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidNodeConfig(
|
||||
name,
|
||||
"command".to_string(),
|
||||
));
|
||||
};
|
||||
let name = name.into();
|
||||
// Check first if the command is set at node level, then try with the default
|
||||
let command = if let Some(cmd) = options.command {
|
||||
cmd.clone()
|
||||
} else if let Some(cmd) = chain_context.default_command {
|
||||
cmd.clone()
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidNodeConfig(name, "command".to_string()));
|
||||
};
|
||||
|
||||
let subcommand = options.subcommand.clone();
|
||||
let subcommand = options.subcommand.clone();
|
||||
|
||||
// If `args` is set at `node` level use them
|
||||
// otherwise use the default_args (can be empty).
|
||||
let args: Vec<Arg> = if options.args.is_empty() {
|
||||
chain_context
|
||||
.default_args
|
||||
.iter()
|
||||
.map(|x| x.to_owned().clone())
|
||||
.collect()
|
||||
} else {
|
||||
options.args
|
||||
};
|
||||
// If `args` is set at `node` level use them
|
||||
// otherwise use the default_args (can be empty).
|
||||
let args: Vec<Arg> = if options.args.is_empty() {
|
||||
chain_context.default_args.iter().map(|x| x.to_owned().clone()).collect()
|
||||
} else {
|
||||
options.args
|
||||
};
|
||||
|
||||
let (key, peer_id) = generators::generate_node_identity(&name)?;
|
||||
let (key, peer_id) = generators::generate_node_identity(&name)?;
|
||||
|
||||
let mut name_capitalized = name.clone();
|
||||
let seed = format!(
|
||||
"//{}{name_capitalized}",
|
||||
name_capitalized.remove(0).to_uppercase()
|
||||
);
|
||||
let accounts = generators::generate_node_keys(&seed)?;
|
||||
let mut accounts = NodeAccounts { seed, accounts };
|
||||
let mut name_capitalized = name.clone();
|
||||
let seed = format!("//{}{name_capitalized}", name_capitalized.remove(0).to_uppercase());
|
||||
let accounts = generators::generate_node_keys(&seed)?;
|
||||
let mut accounts = NodeAccounts { seed, accounts };
|
||||
|
||||
if evm_based {
|
||||
if let Some(session_key) = options.override_eth_key.as_ref() {
|
||||
accounts
|
||||
.accounts
|
||||
.insert("eth".into(), NodeAccount::new(session_key, session_key));
|
||||
}
|
||||
}
|
||||
if evm_based {
|
||||
if let Some(session_key) = options.override_eth_key.as_ref() {
|
||||
accounts.accounts.insert("eth".into(), NodeAccount::new(session_key, session_key));
|
||||
}
|
||||
}
|
||||
|
||||
let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present {
|
||||
(
|
||||
Some(generators::generate_node_port(None)?),
|
||||
Some(generators::generate_node_port(None)?),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present {
|
||||
(
|
||||
Some(generators::generate_node_port(None)?),
|
||||
Some(generators::generate_node_port(None)?),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
//
|
||||
Ok(Self {
|
||||
name,
|
||||
key,
|
||||
peer_id,
|
||||
image,
|
||||
command,
|
||||
subcommand,
|
||||
args,
|
||||
available_args_output: None,
|
||||
is_validator: options.is_validator,
|
||||
is_invulnerable: false,
|
||||
is_bootnode: false,
|
||||
initial_balance: 0,
|
||||
env: options.env,
|
||||
bootnodes_addresses: vec![],
|
||||
resources: None,
|
||||
p2p_cert_hash: None,
|
||||
db_snapshot: None,
|
||||
accounts,
|
||||
// should be deprecated now!
|
||||
ws_port: generators::generate_node_port(None)?,
|
||||
rpc_port: generators::generate_node_port(options.rpc_port)?,
|
||||
prometheus_port: generators::generate_node_port(options.prometheus_port)?,
|
||||
p2p_port: generators::generate_node_port(options.p2p_port)?,
|
||||
full_node_p2p_port,
|
||||
full_node_prometheus_port,
|
||||
node_log_path: None,
|
||||
keystore_path: None,
|
||||
keystore_key_types: vec![],
|
||||
})
|
||||
}
|
||||
//
|
||||
Ok(Self {
|
||||
name,
|
||||
key,
|
||||
peer_id,
|
||||
image,
|
||||
command,
|
||||
subcommand,
|
||||
args,
|
||||
available_args_output: None,
|
||||
is_validator: options.is_validator,
|
||||
is_invulnerable: false,
|
||||
is_bootnode: false,
|
||||
initial_balance: 0,
|
||||
env: options.env,
|
||||
bootnodes_addresses: vec![],
|
||||
resources: None,
|
||||
p2p_cert_hash: None,
|
||||
db_snapshot: None,
|
||||
accounts,
|
||||
// should be deprecated now!
|
||||
ws_port: generators::generate_node_port(None)?,
|
||||
rpc_port: generators::generate_node_port(options.rpc_port)?,
|
||||
prometheus_port: generators::generate_node_port(options.prometheus_port)?,
|
||||
p2p_port: generators::generate_node_port(options.p2p_port)?,
|
||||
full_node_p2p_port,
|
||||
full_node_prometheus_port,
|
||||
node_log_path: None,
|
||||
keystore_path: None,
|
||||
keystore_key_types: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn supports_arg(&self, arg: impl AsRef<str>) -> bool {
|
||||
self.available_args_output
|
||||
.as_ref()
|
||||
.expect(&format!(
|
||||
"available args should be present at this point {THIS_IS_A_BUG}"
|
||||
))
|
||||
.contains(arg.as_ref())
|
||||
}
|
||||
pub(crate) fn supports_arg(&self, arg: impl AsRef<str>) -> bool {
|
||||
self.available_args_output
|
||||
.as_ref()
|
||||
.expect(&format!("available args should be present at this point {THIS_IS_A_BUG}"))
|
||||
.contains(arg.as_ref())
|
||||
}
|
||||
|
||||
pub fn command(&self) -> &str {
|
||||
self.command.as_str()
|
||||
}
|
||||
pub fn command(&self) -> &str {
|
||||
self.command.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
+139
-139
@@ -1,181 +1,181 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use configuration::{
|
||||
shared::{
|
||||
helpers::generate_unique_node_name_from_names,
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Chain, Command, Image},
|
||||
},
|
||||
types::JsonOverrides,
|
||||
NodeConfig, RelaychainConfig,
|
||||
shared::{
|
||||
helpers::generate_unique_node_name_from_names,
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Chain, Command, Image},
|
||||
},
|
||||
types::JsonOverrides,
|
||||
NodeConfig, RelaychainConfig,
|
||||
};
|
||||
use serde::{Deserialize, 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},
|
||||
errors::OrchestratorError,
|
||||
generators::chain_spec::{ChainSpec, Context},
|
||||
shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
|
||||
};
|
||||
|
||||
/// A relaychain configuration spec
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RelaychainSpec {
|
||||
/// Chain to use (e.g. rococo-local).
|
||||
pub(crate) chain: Chain,
|
||||
/// 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 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 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 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 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>,
|
||||
/// 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,
|
||||
// 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 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,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// Wasm override path/url to use.
|
||||
pub(crate) wasm_override: Option<AssetLocation>,
|
||||
|
||||
/// Nodes to run.
|
||||
pub(crate) nodes: Vec<NodeSpec>,
|
||||
/// Nodes to run.
|
||||
pub(crate) nodes: Vec<NodeSpec>,
|
||||
|
||||
/// Raw chain-spec override path, url or inline json to use.
|
||||
pub(crate) raw_spec_override: Option<JsonOverrides>,
|
||||
/// Raw chain-spec override path, url or inline json to use.
|
||||
pub(crate) raw_spec_override: Option<JsonOverrides>,
|
||||
}
|
||||
|
||||
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(),
|
||||
))?;
|
||||
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());
|
||||
// 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 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(),
|
||||
config.chain_spec_command_output_path(),
|
||||
)
|
||||
.image(main_image.clone());
|
||||
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(),
|
||||
config.chain_spec_command_output_path(),
|
||||
)
|
||||
.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
|
||||
};
|
||||
// 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
|
||||
};
|
||||
|
||||
// add chain-spec runtime if present
|
||||
let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() {
|
||||
chain_spec.runtime(chain_spec_runtime.clone())
|
||||
} else {
|
||||
chain_spec
|
||||
};
|
||||
// add chain-spec runtime if present
|
||||
let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() {
|
||||
chain_spec.runtime(chain_spec_runtime.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(),
|
||||
};
|
||||
// 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 mut nodes: Vec<NodeConfig> = config.nodes().into_iter().cloned().collect();
|
||||
nodes.extend(
|
||||
config
|
||||
.group_node_configs()
|
||||
.into_iter()
|
||||
.flat_map(|node_group| node_group.expand_group_configs()),
|
||||
);
|
||||
let mut nodes: Vec<NodeConfig> = config.nodes().into_iter().cloned().collect();
|
||||
nodes.extend(
|
||||
config
|
||||
.group_node_configs()
|
||||
.into_iter()
|
||||
.flat_map(|node_group| node_group.expand_group_configs()),
|
||||
);
|
||||
|
||||
let mut names = HashSet::new();
|
||||
let (nodes, mut errs) = nodes
|
||||
.iter()
|
||||
.map(|node_config| NodeSpec::from_config(node_config, &chain_context, false, false))
|
||||
.fold((vec![], vec![]), |(mut nodes, mut errs), result| {
|
||||
match result {
|
||||
Ok(mut node) => {
|
||||
let unique_name =
|
||||
generate_unique_node_name_from_names(node.name, &mut names);
|
||||
node.name = unique_name;
|
||||
nodes.push(node);
|
||||
},
|
||||
Err(err) => errs.push(err),
|
||||
}
|
||||
(nodes, errs)
|
||||
});
|
||||
let mut names = HashSet::new();
|
||||
let (nodes, mut errs) = nodes
|
||||
.iter()
|
||||
.map(|node_config| NodeSpec::from_config(node_config, &chain_context, false, false))
|
||||
.fold((vec![], vec![]), |(mut nodes, mut errs), result| {
|
||||
match result {
|
||||
Ok(mut node) => {
|
||||
let unique_name =
|
||||
generate_unique_node_name_from_names(node.name, &mut names);
|
||||
node.name = unique_name;
|
||||
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));
|
||||
}
|
||||
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,
|
||||
raw_spec_override: config.raw_spec_override().cloned(),
|
||||
})
|
||||
}
|
||||
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,
|
||||
raw_spec_override: config.raw_spec_override().cloned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chain_spec(&self) -> &ChainSpec {
|
||||
&self.chain_spec
|
||||
}
|
||||
pub fn chain_spec(&self) -> &ChainSpec {
|
||||
&self.chain_spec
|
||||
}
|
||||
|
||||
pub fn chain_spec_mut(&mut self) -> &mut ChainSpec {
|
||||
&mut self.chain_spec
|
||||
}
|
||||
pub fn chain_spec_mut(&mut self) -> &mut ChainSpec {
|
||||
&mut self.chain_spec
|
||||
}
|
||||
}
|
||||
|
||||
+294
-318
@@ -1,12 +1,12 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use configuration::{
|
||||
shared::{helpers::generate_unique_node_name_from_names, resources::Resources},
|
||||
types::{Arg, AssetLocation, Chain, Command, Image, JsonOverrides},
|
||||
NodeConfig, ParachainConfig, RegistrationStrategy,
|
||||
shared::{helpers::generate_unique_node_name_from_names, resources::Resources},
|
||||
types::{Arg, AssetLocation, Chain, Command, Image, JsonOverrides},
|
||||
NodeConfig, ParachainConfig, RegistrationStrategy,
|
||||
};
|
||||
use provider::DynNamespace;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -15,372 +15,348 @@ use tracing::debug;
|
||||
|
||||
use super::node::NodeSpec;
|
||||
use crate::{
|
||||
errors::OrchestratorError,
|
||||
generators::{
|
||||
chain_spec::{ChainSpec, Context, ParaGenesisConfig},
|
||||
para_artifact::*,
|
||||
},
|
||||
shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
|
||||
ScopedFilesystem,
|
||||
errors::OrchestratorError,
|
||||
generators::{
|
||||
chain_spec::{ChainSpec, Context, ParaGenesisConfig},
|
||||
para_artifact::*,
|
||||
},
|
||||
shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
|
||||
ScopedFilesystem,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TeyrchainSpec {
|
||||
// `name` of the parachain (used in some corner cases)
|
||||
// name: Option<Chain>,
|
||||
/// Parachain id
|
||||
pub(crate) id: u32,
|
||||
// `name` of the parachain (used in some corner cases)
|
||||
// name: Option<Chain>,
|
||||
/// Parachain id
|
||||
pub(crate) id: u32,
|
||||
|
||||
/// Unique id of the parachain, in the patter of <para_id>-<n>
|
||||
/// where the suffix is only present if more than one parachain is set with the same id
|
||||
pub(crate) unique_id: String,
|
||||
/// Unique id of the parachain, in the patter of <para_id>-<n>
|
||||
/// where the suffix is only present if more than one parachain is set with the same id
|
||||
pub(crate) unique_id: String,
|
||||
|
||||
/// Default command to run the node. Can be overridden on each node.
|
||||
pub(crate) default_command: Option<Command>,
|
||||
/// 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 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 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 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>,
|
||||
/// Default arguments to use in nodes. Can be overridden on each node.
|
||||
pub(crate) default_args: Vec<Arg>,
|
||||
|
||||
/// Chain-spec, only needed by cumulus based paras
|
||||
pub(crate) chain_spec: Option<ChainSpec>,
|
||||
/// Chain-spec, only needed by cumulus based paras
|
||||
pub(crate) chain_spec: Option<ChainSpec>,
|
||||
|
||||
/// Do not automatically assign a bootnode role if no nodes are marked as bootnodes.
|
||||
pub(crate) no_default_bootnodes: bool,
|
||||
/// Do not automatically assign a bootnode role if no nodes are marked as bootnodes.
|
||||
pub(crate) no_default_bootnodes: bool,
|
||||
|
||||
/// Registration strategy to use
|
||||
pub(crate) registration_strategy: RegistrationStrategy,
|
||||
/// Registration strategy to use
|
||||
pub(crate) registration_strategy: RegistrationStrategy,
|
||||
|
||||
/// Onboard as parachain or parathread
|
||||
pub(crate) onboard_as_parachain: bool,
|
||||
/// Onboard as parachain or parathread
|
||||
pub(crate) onboard_as_parachain: bool,
|
||||
|
||||
/// Is the parachain cumulus-based
|
||||
pub(crate) is_cumulus_based: bool,
|
||||
/// Is the parachain cumulus-based
|
||||
pub(crate) is_cumulus_based: bool,
|
||||
|
||||
/// Is the parachain evm-based
|
||||
pub(crate) is_evm_based: bool,
|
||||
/// Is the parachain evm-based
|
||||
pub(crate) is_evm_based: bool,
|
||||
|
||||
/// Initial balance
|
||||
pub(crate) initial_balance: u128,
|
||||
/// Initial balance
|
||||
pub(crate) initial_balance: u128,
|
||||
|
||||
/// Genesis state (head) to register the parachain
|
||||
pub(crate) genesis_state: ParaArtifact,
|
||||
/// Genesis state (head) to register the parachain
|
||||
pub(crate) genesis_state: ParaArtifact,
|
||||
|
||||
/// Genesis WASM to register the parachain
|
||||
pub(crate) genesis_wasm: ParaArtifact,
|
||||
/// Genesis WASM to register the parachain
|
||||
pub(crate) genesis_wasm: ParaArtifact,
|
||||
|
||||
/// Genesis overrides as JSON value.
|
||||
pub(crate) genesis_overrides: Option<serde_json::Value>,
|
||||
/// Genesis overrides as JSON value.
|
||||
pub(crate) genesis_overrides: Option<serde_json::Value>,
|
||||
|
||||
/// Wasm override path/url to use.
|
||||
pub(crate) wasm_override: Option<AssetLocation>,
|
||||
/// Wasm override path/url to use.
|
||||
pub(crate) wasm_override: Option<AssetLocation>,
|
||||
|
||||
/// Collators to spawn
|
||||
pub(crate) collators: Vec<NodeSpec>,
|
||||
/// Collators to spawn
|
||||
pub(crate) collators: Vec<NodeSpec>,
|
||||
|
||||
/// Raw chain-spec override path, url or inline json to use.
|
||||
pub(crate) raw_spec_override: Option<JsonOverrides>,
|
||||
/// Raw chain-spec override path, url or inline json to use.
|
||||
pub(crate) raw_spec_override: Option<JsonOverrides>,
|
||||
|
||||
/// Bootnodes addresses to use for the parachain nodes
|
||||
pub(crate) bootnodes_addresses: Vec<multiaddr::Multiaddr>,
|
||||
/// Bootnodes addresses to use for the parachain nodes
|
||||
pub(crate) bootnodes_addresses: Vec<multiaddr::Multiaddr>,
|
||||
}
|
||||
|
||||
impl TeyrchainSpec {
|
||||
pub fn from_config(
|
||||
config: &ParachainConfig,
|
||||
relay_chain: Chain,
|
||||
) -> Result<TeyrchainSpec, OrchestratorError> {
|
||||
let main_cmd = if let Some(cmd) = config.default_command() {
|
||||
cmd
|
||||
} else if let Some(first_node) = config.collators().first() {
|
||||
let Some(cmd) = first_node.command() else {
|
||||
return Err(OrchestratorError::InvalidConfig(format!("Parachain {}, either default_command or command in the first node needs to be set.", config.id())));
|
||||
};
|
||||
pub fn from_config(
|
||||
config: &ParachainConfig,
|
||||
relay_chain: Chain,
|
||||
) -> Result<TeyrchainSpec, OrchestratorError> {
|
||||
let main_cmd = if let Some(cmd) = config.default_command() {
|
||||
cmd
|
||||
} else if let Some(first_node) = config.collators().first() {
|
||||
let Some(cmd) = first_node.command() else {
|
||||
return Err(OrchestratorError::InvalidConfig(format!("Parachain {}, either default_command or command in the first node needs to be set.", config.id())));
|
||||
};
|
||||
|
||||
cmd
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidConfig(format!(
|
||||
"Parachain {}, without nodes and default_command isn't set.",
|
||||
config.id()
|
||||
)));
|
||||
};
|
||||
cmd
|
||||
} else {
|
||||
return Err(OrchestratorError::InvalidConfig(format!(
|
||||
"Parachain {}, without nodes and default_command isn't set.",
|
||||
config.id()
|
||||
)));
|
||||
};
|
||||
|
||||
// TODO: internally we use image as String
|
||||
let main_image = config
|
||||
.default_image()
|
||||
.or(config.collators().first().and_then(|node| node.image()))
|
||||
.map(|image| image.as_str().to_string());
|
||||
// TODO: internally we use image as String
|
||||
let main_image = config
|
||||
.default_image()
|
||||
.or(config.collators().first().and_then(|node| node.image()))
|
||||
.map(|image| image.as_str().to_string());
|
||||
|
||||
let chain_spec = if config.is_cumulus_based() {
|
||||
// we need a chain-spec
|
||||
let chain_name = if let Some(chain_name) = config.chain() {
|
||||
chain_name.as_str()
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let chain_spec = if config.is_cumulus_based() {
|
||||
// we need a chain-spec
|
||||
let chain_name =
|
||||
if let Some(chain_name) = config.chain() { chain_name.as_str() } else { "" };
|
||||
|
||||
let chain_spec_builder = if chain_name.is_empty() {
|
||||
// if the chain don't have name use the unique_id for the name of the file
|
||||
ChainSpec::new(
|
||||
config.unique_id().to_string(),
|
||||
Context::Para {
|
||||
relay_chain,
|
||||
para_id: config.id(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let chain_spec_file_name = if config.unique_id().contains('-') {
|
||||
&format!("{}-{}", chain_name, config.unique_id())
|
||||
} else {
|
||||
chain_name
|
||||
};
|
||||
ChainSpec::new(
|
||||
chain_spec_file_name,
|
||||
Context::Para {
|
||||
relay_chain,
|
||||
para_id: config.id(),
|
||||
},
|
||||
)
|
||||
};
|
||||
let chain_spec_builder = chain_spec_builder.set_chain_name(chain_name);
|
||||
let chain_spec_builder = if chain_name.is_empty() {
|
||||
// if the chain don't have name use the unique_id for the name of the file
|
||||
ChainSpec::new(
|
||||
config.unique_id().to_string(),
|
||||
Context::Para { relay_chain, para_id: config.id() },
|
||||
)
|
||||
} else {
|
||||
let chain_spec_file_name = if config.unique_id().contains('-') {
|
||||
&format!("{}-{}", chain_name, config.unique_id())
|
||||
} else {
|
||||
chain_name
|
||||
};
|
||||
ChainSpec::new(
|
||||
chain_spec_file_name,
|
||||
Context::Para { relay_chain, para_id: config.id() },
|
||||
)
|
||||
};
|
||||
let chain_spec_builder = chain_spec_builder.set_chain_name(chain_name);
|
||||
|
||||
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 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 = chain_spec_builder
|
||||
.command(
|
||||
tmpl.as_str(),
|
||||
config.chain_spec_command_is_local(),
|
||||
config.chain_spec_command_output_path(),
|
||||
)
|
||||
.image(main_image.clone());
|
||||
let chain_spec = chain_spec_builder
|
||||
.command(
|
||||
tmpl.as_str(),
|
||||
config.chain_spec_command_is_local(),
|
||||
config.chain_spec_command_output_path(),
|
||||
)
|
||||
.image(main_image.clone());
|
||||
|
||||
let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() {
|
||||
chain_spec.asset_location(chain_spec_path.clone())
|
||||
} else {
|
||||
chain_spec
|
||||
};
|
||||
let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() {
|
||||
chain_spec.asset_location(chain_spec_path.clone())
|
||||
} else {
|
||||
chain_spec
|
||||
};
|
||||
|
||||
// add chain-spec runtime if present
|
||||
let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() {
|
||||
chain_spec.runtime(chain_spec_runtime.clone())
|
||||
} else {
|
||||
chain_spec
|
||||
};
|
||||
// add chain-spec runtime if present
|
||||
let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() {
|
||||
chain_spec.runtime(chain_spec_runtime.clone())
|
||||
} else {
|
||||
chain_spec
|
||||
};
|
||||
|
||||
Some(chain_spec)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(chain_spec)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// 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(),
|
||||
};
|
||||
// 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(),
|
||||
};
|
||||
|
||||
// We want to track the errors for all the nodes and report them ones
|
||||
let mut errs: Vec<OrchestratorError> = Default::default();
|
||||
let mut collators: Vec<NodeSpec> = Default::default();
|
||||
// We want to track the errors for all the nodes and report them ones
|
||||
let mut errs: Vec<OrchestratorError> = Default::default();
|
||||
let mut collators: Vec<NodeSpec> = Default::default();
|
||||
|
||||
let mut nodes: Vec<NodeConfig> = config.collators().into_iter().cloned().collect();
|
||||
nodes.extend(
|
||||
config
|
||||
.group_collators_configs()
|
||||
.into_iter()
|
||||
.flat_map(|node_group| node_group.expand_group_configs()),
|
||||
);
|
||||
let mut nodes: Vec<NodeConfig> = config.collators().into_iter().cloned().collect();
|
||||
nodes.extend(
|
||||
config
|
||||
.group_collators_configs()
|
||||
.into_iter()
|
||||
.flat_map(|node_group| node_group.expand_group_configs()),
|
||||
);
|
||||
|
||||
let mut names = HashSet::new();
|
||||
for node_config in nodes {
|
||||
match NodeSpec::from_config(&node_config, &chain_context, true, config.is_evm_based()) {
|
||||
Ok(mut node) => {
|
||||
let unique_name = generate_unique_node_name_from_names(node.name, &mut names);
|
||||
node.name = unique_name;
|
||||
collators.push(node)
|
||||
},
|
||||
Err(err) => errs.push(err),
|
||||
}
|
||||
}
|
||||
let genesis_state = if let Some(path) = config.genesis_state_path() {
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::State,
|
||||
ParaArtifactBuildOption::Path(path.to_string()),
|
||||
)
|
||||
} else {
|
||||
let cmd = if let Some(cmd) = config.genesis_state_generator() {
|
||||
cmd.cmd()
|
||||
} else {
|
||||
main_cmd
|
||||
};
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::State,
|
||||
ParaArtifactBuildOption::Command(cmd.as_str().into()),
|
||||
)
|
||||
.image(main_image.clone())
|
||||
};
|
||||
let mut names = HashSet::new();
|
||||
for node_config in nodes {
|
||||
match NodeSpec::from_config(&node_config, &chain_context, true, config.is_evm_based()) {
|
||||
Ok(mut node) => {
|
||||
let unique_name = generate_unique_node_name_from_names(node.name, &mut names);
|
||||
node.name = unique_name;
|
||||
collators.push(node)
|
||||
},
|
||||
Err(err) => errs.push(err),
|
||||
}
|
||||
}
|
||||
let genesis_state = if let Some(path) = config.genesis_state_path() {
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::State,
|
||||
ParaArtifactBuildOption::Path(path.to_string()),
|
||||
)
|
||||
} else {
|
||||
let cmd =
|
||||
if let Some(cmd) = config.genesis_state_generator() { cmd.cmd() } else { main_cmd };
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::State,
|
||||
ParaArtifactBuildOption::Command(cmd.as_str().into()),
|
||||
)
|
||||
.image(main_image.clone())
|
||||
};
|
||||
|
||||
let genesis_wasm = if let Some(path) = config.genesis_wasm_path() {
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::Wasm,
|
||||
ParaArtifactBuildOption::Path(path.to_string()),
|
||||
)
|
||||
} else {
|
||||
let cmd = if let Some(cmd) = config.genesis_wasm_generator() {
|
||||
cmd.as_str()
|
||||
} else {
|
||||
main_cmd.as_str()
|
||||
};
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::Wasm,
|
||||
ParaArtifactBuildOption::Command(cmd.into()),
|
||||
)
|
||||
.image(main_image.clone())
|
||||
};
|
||||
let genesis_wasm = if let Some(path) = config.genesis_wasm_path() {
|
||||
ParaArtifact::new(
|
||||
ParaArtifactType::Wasm,
|
||||
ParaArtifactBuildOption::Path(path.to_string()),
|
||||
)
|
||||
} else {
|
||||
let cmd = if let Some(cmd) = config.genesis_wasm_generator() {
|
||||
cmd.as_str()
|
||||
} else {
|
||||
main_cmd.as_str()
|
||||
};
|
||||
ParaArtifact::new(ParaArtifactType::Wasm, ParaArtifactBuildOption::Command(cmd.into()))
|
||||
.image(main_image.clone())
|
||||
};
|
||||
|
||||
let para_spec = TeyrchainSpec {
|
||||
id: config.id(),
|
||||
// ensure unique id is set at this point, if not just set to the para_id
|
||||
unique_id: if config.unique_id().is_empty() {
|
||||
config.id().to_string()
|
||||
} else {
|
||||
config.unique_id().to_string()
|
||||
},
|
||||
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,
|
||||
no_default_bootnodes: config.no_default_bootnodes(),
|
||||
registration_strategy: config
|
||||
.registration_strategy()
|
||||
.unwrap_or(&RegistrationStrategy::InGenesis)
|
||||
.clone(),
|
||||
onboard_as_parachain: config.onboard_as_parachain(),
|
||||
is_cumulus_based: config.is_cumulus_based(),
|
||||
is_evm_based: config.is_evm_based(),
|
||||
initial_balance: config.initial_balance(),
|
||||
genesis_state,
|
||||
genesis_wasm,
|
||||
genesis_overrides: config.genesis_overrides().cloned(),
|
||||
collators,
|
||||
raw_spec_override: config.raw_spec_override().cloned(),
|
||||
bootnodes_addresses: config.bootnodes_addresses().into_iter().cloned().collect(),
|
||||
};
|
||||
let para_spec = TeyrchainSpec {
|
||||
id: config.id(),
|
||||
// ensure unique id is set at this point, if not just set to the para_id
|
||||
unique_id: if config.unique_id().is_empty() {
|
||||
config.id().to_string()
|
||||
} else {
|
||||
config.unique_id().to_string()
|
||||
},
|
||||
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,
|
||||
no_default_bootnodes: config.no_default_bootnodes(),
|
||||
registration_strategy: config
|
||||
.registration_strategy()
|
||||
.unwrap_or(&RegistrationStrategy::InGenesis)
|
||||
.clone(),
|
||||
onboard_as_parachain: config.onboard_as_parachain(),
|
||||
is_cumulus_based: config.is_cumulus_based(),
|
||||
is_evm_based: config.is_evm_based(),
|
||||
initial_balance: config.initial_balance(),
|
||||
genesis_state,
|
||||
genesis_wasm,
|
||||
genesis_overrides: config.genesis_overrides().cloned(),
|
||||
collators,
|
||||
raw_spec_override: config.raw_spec_override().cloned(),
|
||||
bootnodes_addresses: config.bootnodes_addresses().into_iter().cloned().collect(),
|
||||
};
|
||||
|
||||
Ok(para_spec)
|
||||
}
|
||||
Ok(para_spec)
|
||||
}
|
||||
|
||||
pub fn registration_strategy(&self) -> &RegistrationStrategy {
|
||||
&self.registration_strategy
|
||||
}
|
||||
pub fn registration_strategy(&self) -> &RegistrationStrategy {
|
||||
&self.registration_strategy
|
||||
}
|
||||
|
||||
pub fn get_genesis_config(&self) -> Result<ParaGenesisConfig<&PathBuf>, OrchestratorError> {
|
||||
let genesis_config = ParaGenesisConfig {
|
||||
state_path: self.genesis_state.artifact_path().ok_or(
|
||||
OrchestratorError::InvariantError(
|
||||
"artifact path for state must be set at this point",
|
||||
),
|
||||
)?,
|
||||
wasm_path: self.genesis_wasm.artifact_path().ok_or(
|
||||
OrchestratorError::InvariantError(
|
||||
"artifact path for wasm must be set at this point",
|
||||
),
|
||||
)?,
|
||||
id: self.id,
|
||||
as_parachain: self.onboard_as_parachain,
|
||||
};
|
||||
Ok(genesis_config)
|
||||
}
|
||||
pub fn get_genesis_config(&self) -> Result<ParaGenesisConfig<&PathBuf>, OrchestratorError> {
|
||||
let genesis_config = ParaGenesisConfig {
|
||||
state_path: self.genesis_state.artifact_path().ok_or(
|
||||
OrchestratorError::InvariantError(
|
||||
"artifact path for state must be set at this point",
|
||||
),
|
||||
)?,
|
||||
wasm_path: self.genesis_wasm.artifact_path().ok_or(
|
||||
OrchestratorError::InvariantError(
|
||||
"artifact path for wasm must be set at this point",
|
||||
),
|
||||
)?,
|
||||
id: self.id,
|
||||
as_parachain: self.onboard_as_parachain,
|
||||
};
|
||||
Ok(genesis_config)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn chain_spec(&self) -> Option<&ChainSpec> {
|
||||
self.chain_spec.as_ref()
|
||||
}
|
||||
pub fn chain_spec(&self) -> Option<&ChainSpec> {
|
||||
self.chain_spec.as_ref()
|
||||
}
|
||||
|
||||
pub fn chain_spec_mut(&mut self) -> Option<&mut ChainSpec> {
|
||||
self.chain_spec.as_mut()
|
||||
}
|
||||
pub fn chain_spec_mut(&mut self) -> Option<&mut ChainSpec> {
|
||||
self.chain_spec.as_mut()
|
||||
}
|
||||
|
||||
/// Build parachain chain-spec
|
||||
///
|
||||
/// This function customize the chain-spec (if is possible) and build the raw version
|
||||
/// of the chain-spec.
|
||||
pub(crate) async fn build_chain_spec<'a, T>(
|
||||
&mut self,
|
||||
relay_chain_id: &str,
|
||||
ns: &DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
) -> Result<Option<PathBuf>, anyhow::Error>
|
||||
where
|
||||
T: FileSystem,
|
||||
{
|
||||
let cloned = self.clone();
|
||||
let chain_spec_raw_path = if let Some(chain_spec) = self.chain_spec.as_mut() {
|
||||
debug!("parachain chain-spec building!");
|
||||
chain_spec.build(ns, scoped_fs).await?;
|
||||
debug!("parachain chain-spec built!");
|
||||
/// Build parachain chain-spec
|
||||
///
|
||||
/// This function customize the chain-spec (if is possible) and build the raw version
|
||||
/// of the chain-spec.
|
||||
pub(crate) async fn build_chain_spec<'a, T>(
|
||||
&mut self,
|
||||
relay_chain_id: &str,
|
||||
ns: &DynNamespace,
|
||||
scoped_fs: &ScopedFilesystem<'a, T>,
|
||||
) -> Result<Option<PathBuf>, anyhow::Error>
|
||||
where
|
||||
T: FileSystem,
|
||||
{
|
||||
let cloned = self.clone();
|
||||
let chain_spec_raw_path = if let Some(chain_spec) = self.chain_spec.as_mut() {
|
||||
debug!("parachain chain-spec building!");
|
||||
chain_spec.build(ns, scoped_fs).await?;
|
||||
debug!("parachain chain-spec built!");
|
||||
|
||||
chain_spec
|
||||
.customize_para(&cloned, relay_chain_id, scoped_fs)
|
||||
.await?;
|
||||
debug!("parachain chain-spec customized!");
|
||||
chain_spec
|
||||
.build_raw(ns, scoped_fs, Some(relay_chain_id.try_into()?))
|
||||
.await?;
|
||||
debug!("parachain chain-spec raw built!");
|
||||
chain_spec.customize_para(&cloned, relay_chain_id, scoped_fs).await?;
|
||||
debug!("parachain chain-spec customized!");
|
||||
chain_spec.build_raw(ns, scoped_fs, Some(relay_chain_id.try_into()?)).await?;
|
||||
debug!("parachain chain-spec raw built!");
|
||||
|
||||
// override wasm if needed
|
||||
if let Some(ref wasm_override) = self.wasm_override {
|
||||
chain_spec.override_code(scoped_fs, wasm_override).await?;
|
||||
}
|
||||
// override wasm if needed
|
||||
if let Some(ref wasm_override) = self.wasm_override {
|
||||
chain_spec.override_code(scoped_fs, wasm_override).await?;
|
||||
}
|
||||
|
||||
// override raw spec if needed
|
||||
if let Some(ref raw_spec_override) = self.raw_spec_override {
|
||||
chain_spec
|
||||
.override_raw_spec(scoped_fs, raw_spec_override)
|
||||
.await?;
|
||||
}
|
||||
// override raw spec if needed
|
||||
if let Some(ref raw_spec_override) = self.raw_spec_override {
|
||||
chain_spec.override_raw_spec(scoped_fs, raw_spec_override).await?;
|
||||
}
|
||||
|
||||
let chain_spec_raw_path =
|
||||
chain_spec
|
||||
.raw_path()
|
||||
.ok_or(OrchestratorError::InvariantError(
|
||||
"chain-spec raw path should be set now",
|
||||
))?;
|
||||
let chain_spec_raw_path = chain_spec.raw_path().ok_or(
|
||||
OrchestratorError::InvariantError("chain-spec raw path should be set now"),
|
||||
)?;
|
||||
|
||||
Some(chain_spec_raw_path.to_path_buf())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(chain_spec_raw_path)
|
||||
}
|
||||
Some(chain_spec_raw_path.to_path_buf())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(chain_spec_raw_path)
|
||||
}
|
||||
|
||||
/// Get the bootnodes addresses for the parachain spec
|
||||
pub(crate) fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> {
|
||||
self.bootnodes_addresses.iter().collect()
|
||||
}
|
||||
/// Get the bootnodes addresses for the parachain spec
|
||||
pub(crate) fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> {
|
||||
self.bootnodes_addresses.iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ pub const RPC_HTTP_PORT: u16 = 9933;
|
||||
pub const P2P_PORT: u16 = 30333;
|
||||
// default command template to build chain-spec
|
||||
pub const DEFAULT_CHAIN_SPEC_TPL_COMMAND: &str =
|
||||
"{{mainCommand}} build-spec --chain {{chainName}} {{disableBootnodes}}";
|
||||
"{{mainCommand}} build-spec --chain {{chainName}} {{disableBootnodes}}";
|
||||
// interval to determine how often to run node liveness checks
|
||||
pub const NODE_MONITORING_INTERVAL_SECONDS: u64 = 15;
|
||||
// how long to wait before a node is considered unresponsive
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::TcpListener,
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
collections::HashMap,
|
||||
net::TcpListener,
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use configuration::shared::{
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Command, Image, Port},
|
||||
resources::Resources,
|
||||
types::{Arg, AssetLocation, Command, Image, Port},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -15,85 +15,75 @@ pub type Accounts = HashMap<String, NodeAccount>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct NodeAccount {
|
||||
pub address: String,
|
||||
pub public_key: String,
|
||||
pub address: String,
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
impl NodeAccount {
|
||||
pub fn new(addr: impl Into<String>, pk: impl Into<String>) -> Self {
|
||||
Self {
|
||||
address: addr.into(),
|
||||
public_key: pk.into(),
|
||||
}
|
||||
}
|
||||
pub fn new(addr: impl Into<String>, pk: impl Into<String>) -> Self {
|
||||
Self { address: addr.into(), public_key: pk.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct NodeAccounts {
|
||||
pub seed: String,
|
||||
pub accounts: Accounts,
|
||||
pub seed: String,
|
||||
pub accounts: Accounts,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
pub struct ParkedPort(
|
||||
pub(crate) Port,
|
||||
#[serde(skip)] pub(crate) Arc<RwLock<Option<TcpListener>>>,
|
||||
);
|
||||
pub struct ParkedPort(pub(crate) Port, #[serde(skip)] pub(crate) Arc<RwLock<Option<TcpListener>>>);
|
||||
|
||||
impl ParkedPort {
|
||||
pub(crate) fn new(port: u16, listener: TcpListener) -> ParkedPort {
|
||||
let listener = Arc::new(RwLock::new(Some(listener)));
|
||||
ParkedPort(port, listener)
|
||||
}
|
||||
pub(crate) fn new(port: u16, listener: TcpListener) -> ParkedPort {
|
||||
let listener = Arc::new(RwLock::new(Some(listener)));
|
||||
ParkedPort(port, listener)
|
||||
}
|
||||
|
||||
pub(crate) fn drop_listener(&self) {
|
||||
// drop the listener will allow the running node to start listenen connections
|
||||
let mut l = self.1.write().unwrap();
|
||||
*l = None;
|
||||
}
|
||||
pub(crate) fn drop_listener(&self) {
|
||||
// drop the listener will allow the running node to start listenen connections
|
||||
let mut l = self.1.write().unwrap();
|
||||
*l = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ChainDefaultContext<'a> {
|
||||
pub default_command: Option<&'a Command>,
|
||||
pub default_image: Option<&'a Image>,
|
||||
pub default_resources: Option<&'a Resources>,
|
||||
pub default_db_snapshot: Option<&'a AssetLocation>,
|
||||
pub default_args: Vec<&'a Arg>,
|
||||
pub default_command: Option<&'a Command>,
|
||||
pub default_image: Option<&'a Image>,
|
||||
pub default_resources: Option<&'a Resources>,
|
||||
pub default_db_snapshot: Option<&'a AssetLocation>,
|
||||
pub default_args: Vec<&'a Arg>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RegisterParachainOptions {
|
||||
pub id: u32,
|
||||
pub wasm_path: PathBuf,
|
||||
pub state_path: PathBuf,
|
||||
pub node_ws_url: String,
|
||||
pub onboard_as_para: bool,
|
||||
pub seed: Option<[u8; 32]>,
|
||||
pub finalization: bool,
|
||||
pub id: u32,
|
||||
pub wasm_path: PathBuf,
|
||||
pub state_path: PathBuf,
|
||||
pub node_ws_url: String,
|
||||
pub onboard_as_para: bool,
|
||||
pub seed: Option<[u8; 32]>,
|
||||
pub finalization: bool,
|
||||
}
|
||||
|
||||
pub struct RuntimeUpgradeOptions {
|
||||
/// Location of the wasm file (could be either a local file or an url)
|
||||
pub wasm: AssetLocation,
|
||||
/// Name of the node to use as rpc endpoint
|
||||
pub node_name: Option<String>,
|
||||
/// Seed to use to sign and submit (default to //Alice)
|
||||
pub seed: Option<[u8; 32]>,
|
||||
/// Location of the wasm file (could be either a local file or an url)
|
||||
pub wasm: AssetLocation,
|
||||
/// Name of the node to use as rpc endpoint
|
||||
pub node_name: Option<String>,
|
||||
/// Seed to use to sign and submit (default to //Alice)
|
||||
pub seed: Option<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl RuntimeUpgradeOptions {
|
||||
pub fn new(wasm: AssetLocation) -> Self {
|
||||
Self {
|
||||
wasm,
|
||||
node_name: None,
|
||||
seed: None,
|
||||
}
|
||||
}
|
||||
pub fn new(wasm: AssetLocation) -> Self {
|
||||
Self { wasm, node_name: None, seed: None }
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParachainGenesisArgs {
|
||||
pub genesis_head: String,
|
||||
pub validation_code: String,
|
||||
pub parachain: bool,
|
||||
pub genesis_head: String,
|
||||
pub validation_code: String,
|
||||
pub parachain: bool,
|
||||
}
|
||||
|
||||
+222
-252
@@ -3,303 +3,273 @@ use std::{collections::HashMap, path::PathBuf};
|
||||
use anyhow::Context;
|
||||
use configuration::GlobalSettings;
|
||||
use provider::{
|
||||
constants::{LOCALHOST, NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, P2P_PORT},
|
||||
shared::helpers::running_in_ci,
|
||||
types::{SpawnNodeOptions, TransferedFile},
|
||||
DynNamespace,
|
||||
constants::{LOCALHOST, NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, P2P_PORT},
|
||||
shared::helpers::running_in_ci,
|
||||
types::{SpawnNodeOptions, TransferedFile},
|
||||
DynNamespace,
|
||||
};
|
||||
use support::{
|
||||
constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_running_network_replacements,
|
||||
constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_running_network_replacements,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
generators,
|
||||
network::node::NetworkNode,
|
||||
network_spec::{node::NodeSpec, teyrchain::TeyrchainSpec},
|
||||
shared::constants::{FULL_NODE_PROMETHEUS_PORT, PROMETHEUS_PORT, RPC_PORT},
|
||||
ScopedFilesystem, ZombieRole,
|
||||
generators,
|
||||
network::node::NetworkNode,
|
||||
network_spec::{node::NodeSpec, teyrchain::TeyrchainSpec},
|
||||
shared::constants::{FULL_NODE_PROMETHEUS_PORT, PROMETHEUS_PORT, RPC_PORT},
|
||||
ScopedFilesystem, ZombieRole,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SpawnNodeCtx<'a, T: FileSystem> {
|
||||
/// Relaychain id, from the chain-spec (e.g rococo_local_testnet)
|
||||
pub(crate) chain_id: &'a str,
|
||||
// Parachain id, from the chain-spec (e.g local_testnet)
|
||||
pub(crate) parachain_id: Option<&'a str>,
|
||||
/// Relaychain chain name (e.g rococo-local)
|
||||
pub(crate) chain: &'a str,
|
||||
/// Role of the node in the network
|
||||
pub(crate) role: ZombieRole,
|
||||
/// Ref to the namespace
|
||||
pub(crate) ns: &'a DynNamespace,
|
||||
/// Ref to an scoped filesystem (encapsulate fs actions inside the ns directory)
|
||||
pub(crate) scoped_fs: &'a ScopedFilesystem<'a, T>,
|
||||
/// Ref to a parachain (used to spawn collators)
|
||||
pub(crate) parachain: Option<&'a TeyrchainSpec>,
|
||||
/// The string representation of the bootnode address to pass to nodes
|
||||
pub(crate) bootnodes_addr: &'a Vec<String>,
|
||||
/// Flag to wait node is ready or not
|
||||
/// Ready state means we can query Prometheus internal server
|
||||
pub(crate) wait_ready: bool,
|
||||
/// A json representation of the running nodes with their names as 'key'
|
||||
pub(crate) nodes_by_name: serde_json::Value,
|
||||
/// A ref to the global settings
|
||||
pub(crate) global_settings: &'a GlobalSettings,
|
||||
/// Relaychain id, from the chain-spec (e.g rococo_local_testnet)
|
||||
pub(crate) chain_id: &'a str,
|
||||
// Parachain id, from the chain-spec (e.g local_testnet)
|
||||
pub(crate) parachain_id: Option<&'a str>,
|
||||
/// Relaychain chain name (e.g rococo-local)
|
||||
pub(crate) chain: &'a str,
|
||||
/// Role of the node in the network
|
||||
pub(crate) role: ZombieRole,
|
||||
/// Ref to the namespace
|
||||
pub(crate) ns: &'a DynNamespace,
|
||||
/// Ref to an scoped filesystem (encapsulate fs actions inside the ns directory)
|
||||
pub(crate) scoped_fs: &'a ScopedFilesystem<'a, T>,
|
||||
/// Ref to a parachain (used to spawn collators)
|
||||
pub(crate) parachain: Option<&'a TeyrchainSpec>,
|
||||
/// The string representation of the bootnode address to pass to nodes
|
||||
pub(crate) bootnodes_addr: &'a Vec<String>,
|
||||
/// Flag to wait node is ready or not
|
||||
/// Ready state means we can query Prometheus internal server
|
||||
pub(crate) wait_ready: bool,
|
||||
/// A json representation of the running nodes with their names as 'key'
|
||||
pub(crate) nodes_by_name: serde_json::Value,
|
||||
/// A ref to the global settings
|
||||
pub(crate) global_settings: &'a GlobalSettings,
|
||||
}
|
||||
|
||||
pub async fn spawn_node<'a, T>(
|
||||
node: &NodeSpec,
|
||||
mut files_to_inject: Vec<TransferedFile>,
|
||||
ctx: &SpawnNodeCtx<'a, T>,
|
||||
node: &NodeSpec,
|
||||
mut files_to_inject: Vec<TransferedFile>,
|
||||
ctx: &SpawnNodeCtx<'a, T>,
|
||||
) -> Result<NetworkNode, anyhow::Error>
|
||||
where
|
||||
T: FileSystem,
|
||||
T: FileSystem,
|
||||
{
|
||||
let mut created_paths = vec![];
|
||||
// Create and inject the keystore IFF
|
||||
// - The node is validator in the relaychain
|
||||
// - The node is collator (encoded as validator) and the parachain is cumulus_based
|
||||
// (parachain_id) should be set then.
|
||||
if node.is_validator && (ctx.parachain.is_none() || ctx.parachain_id.is_some()) {
|
||||
// Generate keystore for node
|
||||
let node_files_path = if let Some(para) = ctx.parachain {
|
||||
para.id.to_string()
|
||||
} else {
|
||||
node.name.clone()
|
||||
};
|
||||
let asset_hub_polkadot = ctx
|
||||
.parachain_id
|
||||
.map(|id| id.starts_with("asset-hub-polkadot"))
|
||||
.unwrap_or_default();
|
||||
let keystore_key_types = node.keystore_key_types.iter().map(String::as_str).collect();
|
||||
let key_filenames = generators::generate_node_keystore(
|
||||
&node.accounts,
|
||||
&node_files_path,
|
||||
ctx.scoped_fs,
|
||||
asset_hub_polkadot,
|
||||
keystore_key_types,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut created_paths = vec![];
|
||||
// Create and inject the keystore IFF
|
||||
// - The node is validator in the relaychain
|
||||
// - The node is collator (encoded as validator) and the parachain is cumulus_based
|
||||
// (parachain_id) should be set then.
|
||||
if node.is_validator && (ctx.parachain.is_none() || ctx.parachain_id.is_some()) {
|
||||
// Generate keystore for node
|
||||
let node_files_path =
|
||||
if let Some(para) = ctx.parachain { para.id.to_string() } else { node.name.clone() };
|
||||
let asset_hub_polkadot =
|
||||
ctx.parachain_id.map(|id| id.starts_with("asset-hub-polkadot")).unwrap_or_default();
|
||||
let keystore_key_types = node.keystore_key_types.iter().map(String::as_str).collect();
|
||||
let key_filenames = generators::generate_node_keystore(
|
||||
&node.accounts,
|
||||
&node_files_path,
|
||||
ctx.scoped_fs,
|
||||
asset_hub_polkadot,
|
||||
keystore_key_types,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Paths returned are relative to the base dir, we need to convert into
|
||||
// fullpaths to inject them in the nodes.
|
||||
let remote_keystore_chain_id = if let Some(id) = ctx.parachain_id {
|
||||
id
|
||||
} else {
|
||||
ctx.chain_id
|
||||
};
|
||||
// Paths returned are relative to the base dir, we need to convert into
|
||||
// fullpaths to inject them in the nodes.
|
||||
let remote_keystore_chain_id =
|
||||
if let Some(id) = ctx.parachain_id { id } else { ctx.chain_id };
|
||||
|
||||
let keystore_path = node.keystore_path.clone().unwrap_or(PathBuf::from(format!(
|
||||
"/data/chains/{remote_keystore_chain_id}/keystore",
|
||||
)));
|
||||
let keystore_path = node
|
||||
.keystore_path
|
||||
.clone()
|
||||
.unwrap_or(PathBuf::from(format!("/data/chains/{remote_keystore_chain_id}/keystore",)));
|
||||
|
||||
for key_filename in key_filenames {
|
||||
let f = TransferedFile::new(
|
||||
PathBuf::from(format!(
|
||||
"{}/{}/{}",
|
||||
ctx.ns.base_dir().to_string_lossy(),
|
||||
node_files_path,
|
||||
key_filename.to_string_lossy()
|
||||
)),
|
||||
keystore_path.join(key_filename),
|
||||
);
|
||||
files_to_inject.push(f);
|
||||
}
|
||||
created_paths.push(keystore_path);
|
||||
}
|
||||
for key_filename in key_filenames {
|
||||
let f = TransferedFile::new(
|
||||
PathBuf::from(format!(
|
||||
"{}/{}/{}",
|
||||
ctx.ns.base_dir().to_string_lossy(),
|
||||
node_files_path,
|
||||
key_filename.to_string_lossy()
|
||||
)),
|
||||
keystore_path.join(key_filename),
|
||||
);
|
||||
files_to_inject.push(f);
|
||||
}
|
||||
created_paths.push(keystore_path);
|
||||
}
|
||||
|
||||
let base_dir = format!("{}/{}", ctx.ns.base_dir().to_string_lossy(), &node.name);
|
||||
let base_dir = format!("{}/{}", ctx.ns.base_dir().to_string_lossy(), &node.name);
|
||||
|
||||
let (cfg_path, data_path, relay_data_path) = if !ctx.ns.capabilities().prefix_with_full_path {
|
||||
(
|
||||
NODE_CONFIG_DIR.into(),
|
||||
NODE_DATA_DIR.into(),
|
||||
NODE_RELAY_DATA_DIR.into(),
|
||||
)
|
||||
} else {
|
||||
let cfg_path = format!("{}{NODE_CONFIG_DIR}", &base_dir);
|
||||
let data_path = format!("{}{NODE_DATA_DIR}", &base_dir);
|
||||
let relay_data_path = format!("{}{NODE_RELAY_DATA_DIR}", &base_dir);
|
||||
(cfg_path, data_path, relay_data_path)
|
||||
};
|
||||
let (cfg_path, data_path, relay_data_path) = if !ctx.ns.capabilities().prefix_with_full_path {
|
||||
(NODE_CONFIG_DIR.into(), NODE_DATA_DIR.into(), NODE_RELAY_DATA_DIR.into())
|
||||
} else {
|
||||
let cfg_path = format!("{}{NODE_CONFIG_DIR}", &base_dir);
|
||||
let data_path = format!("{}{NODE_DATA_DIR}", &base_dir);
|
||||
let relay_data_path = format!("{}{NODE_RELAY_DATA_DIR}", &base_dir);
|
||||
(cfg_path, data_path, relay_data_path)
|
||||
};
|
||||
|
||||
let gen_opts = generators::GenCmdOptions {
|
||||
relay_chain_name: ctx.chain,
|
||||
cfg_path: &cfg_path, // TODO: get from provider/ns
|
||||
data_path: &data_path, // TODO: get from provider
|
||||
relay_data_path: &relay_data_path, // TODO: get from provider
|
||||
use_wrapper: false, // TODO: get from provider
|
||||
bootnode_addr: ctx.bootnodes_addr.clone(),
|
||||
use_default_ports_in_cmd: ctx.ns.capabilities().use_default_ports_in_cmd,
|
||||
// IFF the provider require an image (e.g k8s) we know this is not native
|
||||
is_native: !ctx.ns.capabilities().requires_image,
|
||||
};
|
||||
let gen_opts = generators::GenCmdOptions {
|
||||
relay_chain_name: ctx.chain,
|
||||
cfg_path: &cfg_path, // TODO: get from provider/ns
|
||||
data_path: &data_path, // TODO: get from provider
|
||||
relay_data_path: &relay_data_path, // TODO: get from provider
|
||||
use_wrapper: false, // TODO: get from provider
|
||||
bootnode_addr: ctx.bootnodes_addr.clone(),
|
||||
use_default_ports_in_cmd: ctx.ns.capabilities().use_default_ports_in_cmd,
|
||||
// IFF the provider require an image (e.g k8s) we know this is not native
|
||||
is_native: !ctx.ns.capabilities().requires_image,
|
||||
};
|
||||
|
||||
let mut collator_full_node_prom_port: Option<u16> = None;
|
||||
let mut collator_full_node_prom_port_external: Option<u16> = None;
|
||||
let mut collator_full_node_prom_port: Option<u16> = None;
|
||||
let mut collator_full_node_prom_port_external: Option<u16> = None;
|
||||
|
||||
let (program, args) = match ctx.role {
|
||||
// Collator should be `non-cumulus` one (e.g adder/undying)
|
||||
ZombieRole::Node | ZombieRole::Collator => {
|
||||
let maybe_para_id = ctx.parachain.map(|para| para.id);
|
||||
let (program, args) = match ctx.role {
|
||||
// Collator should be `non-cumulus` one (e.g adder/undying)
|
||||
ZombieRole::Node | ZombieRole::Collator => {
|
||||
let maybe_para_id = ctx.parachain.map(|para| para.id);
|
||||
|
||||
generators::generate_node_command(node, gen_opts, maybe_para_id)
|
||||
},
|
||||
ZombieRole::CumulusCollator => {
|
||||
let para = ctx.parachain.expect(&format!(
|
||||
"parachain must be part of the context {THIS_IS_A_BUG}"
|
||||
));
|
||||
collator_full_node_prom_port = node.full_node_prometheus_port.as_ref().map(|p| p.0);
|
||||
generators::generate_node_command(node, gen_opts, maybe_para_id)
|
||||
},
|
||||
ZombieRole::CumulusCollator => {
|
||||
let para = ctx
|
||||
.parachain
|
||||
.expect(&format!("parachain must be part of the context {THIS_IS_A_BUG}"));
|
||||
collator_full_node_prom_port = node.full_node_prometheus_port.as_ref().map(|p| p.0);
|
||||
|
||||
generators::generate_node_command_cumulus(node, gen_opts, para.id)
|
||||
},
|
||||
_ => unreachable!(), /* TODO: do we need those?
|
||||
* ZombieRole::Bootnode => todo!(),
|
||||
* ZombieRole::Companion => todo!(), */
|
||||
};
|
||||
generators::generate_node_command_cumulus(node, gen_opts, para.id)
|
||||
},
|
||||
_ => unreachable!(), /* TODO: do we need those?
|
||||
* ZombieRole::Bootnode => todo!(),
|
||||
* ZombieRole::Companion => todo!(), */
|
||||
};
|
||||
|
||||
// apply running networ replacements
|
||||
let args: Vec<String> = args
|
||||
.iter()
|
||||
.map(|arg| apply_running_network_replacements(arg, &ctx.nodes_by_name))
|
||||
.collect();
|
||||
// apply running networ replacements
|
||||
let args: Vec<String> = args
|
||||
.iter()
|
||||
.map(|arg| apply_running_network_replacements(arg, &ctx.nodes_by_name))
|
||||
.collect();
|
||||
|
||||
info!(
|
||||
"🚀 {}, spawning.... with command: {} {}",
|
||||
node.name,
|
||||
program,
|
||||
args.join(" ")
|
||||
);
|
||||
info!("🚀 {}, spawning.... with command: {} {}", node.name, program, args.join(" "));
|
||||
|
||||
let ports = if ctx.ns.capabilities().use_default_ports_in_cmd {
|
||||
// should use default ports to as internal
|
||||
[
|
||||
(P2P_PORT, node.p2p_port.0),
|
||||
(RPC_PORT, node.rpc_port.0),
|
||||
(PROMETHEUS_PORT, node.prometheus_port.0),
|
||||
]
|
||||
} else {
|
||||
[
|
||||
(P2P_PORT, P2P_PORT),
|
||||
(RPC_PORT, RPC_PORT),
|
||||
(PROMETHEUS_PORT, PROMETHEUS_PORT),
|
||||
]
|
||||
};
|
||||
let ports = if ctx.ns.capabilities().use_default_ports_in_cmd {
|
||||
// should use default ports to as internal
|
||||
[
|
||||
(P2P_PORT, node.p2p_port.0),
|
||||
(RPC_PORT, node.rpc_port.0),
|
||||
(PROMETHEUS_PORT, node.prometheus_port.0),
|
||||
]
|
||||
} else {
|
||||
[(P2P_PORT, P2P_PORT), (RPC_PORT, RPC_PORT), (PROMETHEUS_PORT, PROMETHEUS_PORT)]
|
||||
};
|
||||
|
||||
let spawn_ops = SpawnNodeOptions::new(node.name.clone(), program)
|
||||
.args(args)
|
||||
.env(
|
||||
node.env
|
||||
.iter()
|
||||
.map(|var| (var.name.clone(), var.value.clone())),
|
||||
)
|
||||
.injected_files(files_to_inject)
|
||||
.created_paths(created_paths)
|
||||
.db_snapshot(node.db_snapshot.clone())
|
||||
.port_mapping(HashMap::from(ports))
|
||||
.node_log_path(node.node_log_path.clone());
|
||||
let spawn_ops = SpawnNodeOptions::new(node.name.clone(), program)
|
||||
.args(args)
|
||||
.env(node.env.iter().map(|var| (var.name.clone(), var.value.clone())))
|
||||
.injected_files(files_to_inject)
|
||||
.created_paths(created_paths)
|
||||
.db_snapshot(node.db_snapshot.clone())
|
||||
.port_mapping(HashMap::from(ports))
|
||||
.node_log_path(node.node_log_path.clone());
|
||||
|
||||
let spawn_ops = if let Some(image) = node.image.as_ref() {
|
||||
spawn_ops.image(image.as_str())
|
||||
} else {
|
||||
spawn_ops
|
||||
};
|
||||
let spawn_ops = if let Some(image) = node.image.as_ref() {
|
||||
spawn_ops.image(image.as_str())
|
||||
} else {
|
||||
spawn_ops
|
||||
};
|
||||
|
||||
// Drops the port parking listeners before spawn
|
||||
node.ws_port.drop_listener();
|
||||
node.p2p_port.drop_listener();
|
||||
node.rpc_port.drop_listener();
|
||||
node.prometheus_port.drop_listener();
|
||||
if let Some(port) = &node.full_node_p2p_port {
|
||||
port.drop_listener();
|
||||
}
|
||||
if let Some(port) = &node.full_node_prometheus_port {
|
||||
port.drop_listener();
|
||||
}
|
||||
// Drops the port parking listeners before spawn
|
||||
node.ws_port.drop_listener();
|
||||
node.p2p_port.drop_listener();
|
||||
node.rpc_port.drop_listener();
|
||||
node.prometheus_port.drop_listener();
|
||||
if let Some(port) = &node.full_node_p2p_port {
|
||||
port.drop_listener();
|
||||
}
|
||||
if let Some(port) = &node.full_node_prometheus_port {
|
||||
port.drop_listener();
|
||||
}
|
||||
|
||||
let running_node = ctx.ns.spawn_node(&spawn_ops).await.with_context(|| {
|
||||
format!(
|
||||
"Failed to spawn node: {} with opts: {:#?}",
|
||||
node.name, spawn_ops
|
||||
)
|
||||
})?;
|
||||
let running_node = ctx.ns.spawn_node(&spawn_ops).await.with_context(|| {
|
||||
format!("Failed to spawn node: {} with opts: {:#?}", node.name, spawn_ops)
|
||||
})?;
|
||||
|
||||
let mut ip_to_use = if let Some(local_ip) = ctx.global_settings.local_ip() {
|
||||
*local_ip
|
||||
} else {
|
||||
LOCALHOST
|
||||
};
|
||||
let mut ip_to_use =
|
||||
if let Some(local_ip) = ctx.global_settings.local_ip() { *local_ip } else { LOCALHOST };
|
||||
|
||||
let (rpc_port_external, prometheus_port_external, p2p_external);
|
||||
let (rpc_port_external, prometheus_port_external, p2p_external);
|
||||
|
||||
if running_in_ci() && ctx.ns.provider_name() == "k8s" {
|
||||
// running kubernets in ci require to use ip and default port
|
||||
(rpc_port_external, prometheus_port_external, p2p_external) =
|
||||
(RPC_PORT, PROMETHEUS_PORT, P2P_PORT);
|
||||
collator_full_node_prom_port_external = Some(FULL_NODE_PROMETHEUS_PORT);
|
||||
ip_to_use = running_node.ip().await?;
|
||||
} else {
|
||||
// Create port-forward iff we are not in CI or provider doesn't use the default ports (native)
|
||||
let ports = futures::future::try_join_all(vec![
|
||||
running_node.create_port_forward(node.rpc_port.0, RPC_PORT),
|
||||
running_node.create_port_forward(node.prometheus_port.0, PROMETHEUS_PORT),
|
||||
])
|
||||
.await?;
|
||||
if running_in_ci() && ctx.ns.provider_name() == "k8s" {
|
||||
// running kubernets in ci require to use ip and default port
|
||||
(rpc_port_external, prometheus_port_external, p2p_external) =
|
||||
(RPC_PORT, PROMETHEUS_PORT, P2P_PORT);
|
||||
collator_full_node_prom_port_external = Some(FULL_NODE_PROMETHEUS_PORT);
|
||||
ip_to_use = running_node.ip().await?;
|
||||
} else {
|
||||
// Create port-forward iff we are not in CI or provider doesn't use the default ports (native)
|
||||
let ports = futures::future::try_join_all(vec![
|
||||
running_node.create_port_forward(node.rpc_port.0, RPC_PORT),
|
||||
running_node.create_port_forward(node.prometheus_port.0, PROMETHEUS_PORT),
|
||||
])
|
||||
.await?;
|
||||
|
||||
(rpc_port_external, prometheus_port_external, p2p_external) = (
|
||||
ports[0].unwrap_or(node.rpc_port.0),
|
||||
ports[1].unwrap_or(node.prometheus_port.0),
|
||||
// p2p don't need port-fwd
|
||||
node.p2p_port.0,
|
||||
);
|
||||
(rpc_port_external, prometheus_port_external, p2p_external) = (
|
||||
ports[0].unwrap_or(node.rpc_port.0),
|
||||
ports[1].unwrap_or(node.prometheus_port.0),
|
||||
// p2p don't need port-fwd
|
||||
node.p2p_port.0,
|
||||
);
|
||||
|
||||
if let Some(full_node_prom_port) = collator_full_node_prom_port {
|
||||
let port_fwd = running_node
|
||||
.create_port_forward(full_node_prom_port, FULL_NODE_PROMETHEUS_PORT)
|
||||
.await?;
|
||||
collator_full_node_prom_port_external = Some(port_fwd.unwrap_or(full_node_prom_port));
|
||||
}
|
||||
}
|
||||
if let Some(full_node_prom_port) = collator_full_node_prom_port {
|
||||
let port_fwd = running_node
|
||||
.create_port_forward(full_node_prom_port, FULL_NODE_PROMETHEUS_PORT)
|
||||
.await?;
|
||||
collator_full_node_prom_port_external = Some(port_fwd.unwrap_or(full_node_prom_port));
|
||||
}
|
||||
}
|
||||
|
||||
let multiaddr = generators::generate_node_bootnode_addr(
|
||||
&node.peer_id,
|
||||
&running_node.ip().await?,
|
||||
p2p_external,
|
||||
running_node.args().as_ref(),
|
||||
&node.p2p_cert_hash,
|
||||
)?;
|
||||
let multiaddr = generators::generate_node_bootnode_addr(
|
||||
&node.peer_id,
|
||||
&running_node.ip().await?,
|
||||
p2p_external,
|
||||
running_node.args().as_ref(),
|
||||
&node.p2p_cert_hash,
|
||||
)?;
|
||||
|
||||
let ws_uri = format!("ws://{ip_to_use}:{rpc_port_external}");
|
||||
let prometheus_uri = format!("http://{ip_to_use}:{prometheus_port_external}/metrics");
|
||||
info!("🚀 {}, should be running now", node.name);
|
||||
info!(
|
||||
"💻 {}: direct link (pjs) https://polkadot.js.org/apps/?rpc={ws_uri}#/explorer",
|
||||
node.name
|
||||
);
|
||||
info!(
|
||||
let ws_uri = format!("ws://{ip_to_use}:{rpc_port_external}");
|
||||
let prometheus_uri = format!("http://{ip_to_use}:{prometheus_port_external}/metrics");
|
||||
info!("🚀 {}, should be running now", node.name);
|
||||
info!(
|
||||
"💻 {}: direct link (pjs) https://polkadot.js.org/apps/?rpc={ws_uri}#/explorer",
|
||||
node.name
|
||||
);
|
||||
info!(
|
||||
"💻 {}: direct link (papi) https://dev.papi.how/explorer#networkId=custom&endpoint={ws_uri}",
|
||||
node.name
|
||||
);
|
||||
|
||||
info!("📊 {}: metrics link {prometheus_uri}", node.name);
|
||||
info!("📊 {}: metrics link {prometheus_uri}", node.name);
|
||||
|
||||
if let Some(full_node_prom_port) = collator_full_node_prom_port_external {
|
||||
info!(
|
||||
"📊 {}: collator full-node metrics link http://{}:{}/metrics",
|
||||
node.name, ip_to_use, full_node_prom_port
|
||||
);
|
||||
}
|
||||
if let Some(full_node_prom_port) = collator_full_node_prom_port_external {
|
||||
info!(
|
||||
"📊 {}: collator full-node metrics link http://{}:{}/metrics",
|
||||
node.name, ip_to_use, full_node_prom_port
|
||||
);
|
||||
}
|
||||
|
||||
info!("📓 logs cmd: {}", running_node.log_cmd());
|
||||
info!("📓 logs cmd: {}", running_node.log_cmd());
|
||||
|
||||
Ok(NetworkNode::new(
|
||||
node.name.clone(),
|
||||
ws_uri,
|
||||
prometheus_uri,
|
||||
multiaddr,
|
||||
node.clone(),
|
||||
running_node,
|
||||
))
|
||||
Ok(NetworkNode::new(
|
||||
node.name.clone(),
|
||||
ws_uri,
|
||||
prometheus_uri,
|
||||
multiaddr,
|
||||
node.clone(),
|
||||
running_node,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -2,42 +2,38 @@ use pezkuwi_subxt::{backend::rpc::RpcClient, OnlineClient};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ClientFromUrl: Sized {
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error>;
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error>;
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error>;
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<Config: pezkuwi_subxt::Config + Send + Sync> ClientFromUrl for OnlineClient<Config> {
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_url(url).await.map_err(Into::into)
|
||||
}
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_url(url).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_insecure_url(url).await.map_err(Into::into)
|
||||
}
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_insecure_url(url).await.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ClientFromUrl for RpcClient {
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_url(url)
|
||||
.await
|
||||
.map_err(pezkuwi_subxt::Error::from)
|
||||
}
|
||||
async fn from_secure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_url(url).await.map_err(pezkuwi_subxt::Error::from)
|
||||
}
|
||||
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_insecure_url(url)
|
||||
.await
|
||||
.map_err(pezkuwi_subxt::Error::from)
|
||||
}
|
||||
async fn from_insecure_url(url: &str) -> Result<Self, pezkuwi_subxt::Error> {
|
||||
Self::from_insecure_url(url).await.map_err(pezkuwi_subxt::Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_client_from_url<T: ClientFromUrl + Send>(
|
||||
url: &str,
|
||||
url: &str,
|
||||
) -> Result<T, pezkuwi_subxt::Error> {
|
||||
if pezkuwi_subxt::utils::url_is_secure(url)? {
|
||||
T::from_secure_url(url).await
|
||||
} else {
|
||||
T::from_insecure_url(url).await
|
||||
}
|
||||
if pezkuwi_subxt::utils::url_is_secure(url)? {
|
||||
T::from_secure_url(url).await
|
||||
} else {
|
||||
T::from_insecure_url(url).await
|
||||
}
|
||||
}
|
||||
|
||||
+42
-55
@@ -5,65 +5,52 @@ use tracing::{debug, info};
|
||||
use crate::network::node::NetworkNode;
|
||||
|
||||
pub async fn upgrade(
|
||||
node: &NetworkNode,
|
||||
wasm_data: &[u8],
|
||||
sudo: &Keypair,
|
||||
node: &NetworkNode,
|
||||
wasm_data: &[u8],
|
||||
sudo: &Keypair,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
debug!(
|
||||
"Upgrading runtime, using node: {} with endpoting {}",
|
||||
node.name, node.ws_uri
|
||||
);
|
||||
let api: OnlineClient<BizinikiwConfig> = node.wait_client().await?;
|
||||
debug!("Upgrading runtime, using node: {} with endpoting {}", node.name, node.ws_uri);
|
||||
let api: OnlineClient<BizinikiwConfig> = node.wait_client().await?;
|
||||
|
||||
let upgrade = pezkuwi_subxt::dynamic::tx(
|
||||
"System",
|
||||
"set_code_without_checks",
|
||||
vec![Value::from_bytes(wasm_data)],
|
||||
);
|
||||
let upgrade = pezkuwi_subxt::dynamic::tx(
|
||||
"System",
|
||||
"set_code_without_checks",
|
||||
vec![Value::from_bytes(wasm_data)],
|
||||
);
|
||||
|
||||
let sudo_call = pezkuwi_subxt::dynamic::tx(
|
||||
"Sudo",
|
||||
"sudo_unchecked_weight",
|
||||
vec![
|
||||
upgrade.into_value(),
|
||||
Value::named_composite([
|
||||
("ref_time", Value::primitive(1.into())),
|
||||
("proof_size", Value::primitive(1.into())),
|
||||
]),
|
||||
],
|
||||
);
|
||||
let sudo_call = pezkuwi_subxt::dynamic::tx(
|
||||
"Sudo",
|
||||
"sudo_unchecked_weight",
|
||||
vec![
|
||||
upgrade.into_value(),
|
||||
Value::named_composite([
|
||||
("ref_time", Value::primitive(1.into())),
|
||||
("proof_size", Value::primitive(1.into())),
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
let mut tx = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&sudo_call, sudo)
|
||||
.await?;
|
||||
let mut tx = api.tx().sign_and_submit_then_watch_default(&sudo_call, sudo).await?;
|
||||
|
||||
// Below we use the low level API to replicate the `wait_for_in_block` behaviour
|
||||
// which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237.
|
||||
while let Some(status) = tx.next().await {
|
||||
let status = status?;
|
||||
match &status {
|
||||
TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => {
|
||||
let _result = tx_in_block.wait_for_success().await?;
|
||||
let block_status = if status.as_finalized().is_some() {
|
||||
"Finalized"
|
||||
} else {
|
||||
"Best"
|
||||
};
|
||||
info!(
|
||||
"[{}] In block: {:#?}",
|
||||
block_status,
|
||||
tx_in_block.block_hash()
|
||||
);
|
||||
},
|
||||
TxStatus::Error { message }
|
||||
| TxStatus::Invalid { message }
|
||||
| TxStatus::Dropped { message } => {
|
||||
return Err(anyhow::format_err!("Error submitting tx: {message}"));
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
// Below we use the low level API to replicate the `wait_for_in_block` behaviour
|
||||
// which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237.
|
||||
while let Some(status) = tx.next().await {
|
||||
let status = status?;
|
||||
match &status {
|
||||
TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => {
|
||||
let _result = tx_in_block.wait_for_success().await?;
|
||||
let block_status =
|
||||
if status.as_finalized().is_some() { "Finalized" } else { "Best" };
|
||||
info!("[{}] In block: {:#?}", block_status, tx_in_block.block_hash());
|
||||
},
|
||||
TxStatus::Error { message }
|
||||
| TxStatus::Invalid { message }
|
||||
| TxStatus::Dropped { message } => {
|
||||
return Err(anyhow::format_err!("Error submitting tx: {message}"));
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use serde::Deserializer;
|
||||
|
||||
pub fn default_as_empty_vec<'de, D, T>(_deserializer: D) -> Result<Vec<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Vec::new())
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user