use std::path::PathBuf; use config::SideNodeConfig; use crate::{bft_crdt_keys, bitcoin_keys, utils}; pub(crate) mod config; pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io::Error> { ensure_side_directory_exists(&home)?; let (bft_crdt_key_path, bitcoin_key_path, config_path) = utils::side_paths(home.clone()); println!("Writing bft crdt key to: {:?}", bft_crdt_key_path); bft_crdt_keys::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); bitcoin_keys::write(&bitcoin_key_path)?; println!("Writing config to: {:?}", config_path); config::write_toml(&config, &config_path).expect("unable to write config file"); Ok(()) } /// Ensures that the directory at side_dir exists, so we have a place /// to store our key files and config file. fn ensure_side_directory_exists(side_dir: &PathBuf) -> Result<(), std::io::Error> { if side_dir.exists() { return Ok(()); } println!( "Config directory doesn't exist, creating at: {:?}", side_dir ); std::fs::create_dir_all(side_dir) } #[cfg(test)] mod tests { use std::{fs, path::Path, str::FromStr}; use fastcrypto::{ ed25519::Ed25519KeyPair, traits::{EncodeDecodeBase64, KeyPair, ToFromBytes}, }; use super::*; /// Generates a SideNodeConfig with a unique name for each test. /// This is necessary because the tests run in parallel and we /// don't want them to interfere with each other - without a unique /// name, the tests would all try to write to the same directory and we /// get test indeterminacy fn side_node_config() -> (SideNodeConfig, String) { let name = format!("test-{}", uuid::Uuid::new_v4()).to_string(); (SideNodeConfig { name: name.clone() }, name) } #[test] fn creates_bitcoin_keys() { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); let mut bitcoin_keys_path = PathBuf::new(); bitcoin_keys_path.push(side_dir.clone()); bitcoin_keys_path.push(utils::BITCOIN_KEY_FILE); let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); assert!(bitcoin_keys_path.exists()); // check that the pem is readable // let data = fs::read_to_string(bitcoin_keys_path).expect("couldn't read key file"); // let keys = Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file"); // assert_eq!(keys.public().as_bytes().len(), 32); } #[test] fn creates_side_node_directory() { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); let mut test_home = PathBuf::new(); test_home.push(side_dir); let node_dir = Path::new(&test_home).parent().unwrap().to_str().unwrap(); let _ = init(test_home.clone(), config); assert!(std::path::Path::new(node_dir).exists()); } #[test] fn creates_bft_crdt_key_file() { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); let mut key_file_path = PathBuf::new(); key_file_path.push(side_dir.clone()); key_file_path.push(utils::BFT_CRDT_KEY_FILE); let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); assert!(key_file_path.exists()); // check that the pem is readable let data = fs::read_to_string(key_file_path).expect("couldn't read key file"); let keys = Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file"); assert_eq!(keys.public().as_bytes().len(), 32); } #[test] fn creates_config_file() { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); let mut config_file_path = PathBuf::new(); config_file_path.push(side_dir.clone()); config_file_path.push(utils::CONFIG_FILE); let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); assert!(config_file_path.exists()); } }