use super::*;
impl<N: Network> ProgramManager<N> {
pub fn find_program(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
self.find_program_on_disk(program_id).or_else(|_| self.find_program_on_chain(program_id))
}
pub fn find_program_on_disk(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
let local_program_directory =
self.local_program_directory.as_ref().ok_or_else(|| anyhow!("Local program directory not set"))?;
let imports_directory = local_program_directory.join("imports");
ensure!(local_program_directory.exists(), "The program directory does not exist");
ensure!(!Program::is_reserved_keyword(program_id.name()), "Program name is invalid (reserved): {program_id}");
ensure!(
Manifest::<N>::exists_at(local_program_directory),
"Please ensure that the manifest file exists in the Aleo program directory (missing '{}' at '{}')",
Manifest::<N>::file_name(),
local_program_directory.display()
);
let manifest = Manifest::<N>::open(local_program_directory)?;
if manifest.program_id() == program_id {
let package = Package::open(local_program_directory)?;
Ok(package.program().clone())
} else {
let import_file = imports_directory.join(program_id.to_string());
ensure!(
import_file.exists(),
"No program named {program_id:?} found at {:?}",
local_program_directory.display()
);
println!("Attempting to load program {program_id:?} at {:?}", import_file.display());
let mut program_file = File::open(import_file)?;
let mut program_string = String::new();
program_file.read_to_string(&mut program_string).map_err(|err| anyhow::anyhow!(err.to_string()))?;
let program = Program::from_str(&program_string)?;
println!("Loaded program {program_id:?} successfully!");
Ok(program)
}
}
pub fn find_program_on_chain(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
self.api_client()?.get_program(program_id)
}
pub fn find_program_imports(&self, program: &Program<N>) -> Result<Vec<Program<N>>> {
let mut imports = vec![];
for program_id in program.imports().keys() {
if let Ok(program) = self.find_program(program_id) {
imports.push(program);
} else {
bail!("Could not find program import: {:?}", program_id);
}
}
Ok(imports)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
test_utils::{
random_program_id,
setup_directory,
teardown_directory,
HELLO_PROGRAM,
IMPORT_PROGRAM,
RECIPIENT_PRIVATE_KEY,
},
AleoAPIClient,
};
use snarkvm_console::{account::PrivateKey, network::Testnet3};
use std::{ops::Add, panic::catch_unwind, str::FromStr};
#[test]
fn test_file_loading_and_imports() {
let private_key = PrivateKey::<Testnet3>::new(&mut rand::thread_rng()).unwrap();
let credits = Program::<Testnet3>::credits().unwrap().to_string();
let imports = vec![("credits.aleo", credits.as_str()), ("hello.aleo", HELLO_PROGRAM)];
let test_path = setup_directory("aleo_test_file_resolution", IMPORT_PROGRAM, imports).unwrap();
let result = catch_unwind(|| {
let program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, None, Some(test_path.clone()), false).unwrap();
let program_id = ProgramID::<Testnet3>::from_str("aleo_test.aleo").unwrap();
let expected_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
let found_program = program_manager.find_program_on_disk(&program_id).unwrap();
assert_eq!(expected_program, found_program);
let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
let credits_program = Program::<Testnet3>::credits().unwrap();
let imports = program_manager.find_program_imports(&test_program).unwrap();
assert_eq!(imports.len(), 1);
let local_credits_program = &imports[0];
assert_eq!(&credits_program, local_credits_program);
let random_program = random_program_id(16);
let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
assert!(program_manager.find_program_on_disk(&program_id).is_err());
let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
let imports = program_manager.find_program_imports(&bad_import_program);
assert!(imports.is_err());
let credits = Program::<Testnet3>::credits().unwrap();
let imports = program_manager.find_program_imports(&credits).unwrap();
assert_eq!(imports.len(), 0);
});
teardown_directory(&test_path);
assert!(!test_path.exists());
result.unwrap();
}
#[test]
fn test_hybrid_program_and_import_loading() {
let credits_program_string = Program::<Testnet3>::credits().unwrap().to_string();
let imports = vec![("credits.aleo", credits_program_string.as_str())];
let test_path = setup_directory("aleo_test_hybrid_resolution", IMPORT_PROGRAM, imports).unwrap();
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let result = catch_unwind(|| {
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let program_manager = ProgramManager::<Testnet3>::new(
Some(private_key),
None,
Some(api_client),
Some(test_path.clone()),
false,
)
.unwrap();
let program_id = ProgramID::<Testnet3>::from_str("aleo_test.aleo").unwrap();
let expected_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
let found_program = program_manager.find_program(&program_id).unwrap();
assert_eq!(expected_program, found_program);
let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
let credits_program = Program::<Testnet3>::credits().unwrap();
let credits_id = credits_program.id();
let imports = program_manager.find_program_imports(&test_program).unwrap();
assert_eq!(imports.len(), 1);
let local_credits_program = &imports[0];
assert_eq!(&credits_program, local_credits_program);
let random_program = random_program_id(16);
let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
assert!(program_manager.find_program(&program_id).is_err());
assert_eq!(program_manager.find_program(credits_id).unwrap(), credits_program);
let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
let imports = program_manager.find_program_imports(&bad_import_program);
assert!(imports.is_err());
let credits = Program::<Testnet3>::credits().unwrap();
let imports = program_manager.find_program_imports(&credits).unwrap();
assert_eq!(imports.len(), 0);
});
teardown_directory(&test_path);
assert!(!test_path.exists());
result.unwrap();
}
#[test]
fn test_network_program_resolution() {
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
let program_id = ProgramID::<Testnet3>::from_str("credits.aleo").unwrap();
let credits_off_the_chain = Program::<Testnet3>::credits().unwrap();
let credits_on_the_chain = program_manager.find_program_on_chain(&program_id).unwrap();
assert_eq!(credits_off_the_chain, credits_on_the_chain);
}
#[test]
fn test_network_program_imports_are_resolved_correctly() {
let credits = Program::<Testnet3>::credits().unwrap();
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
let imports = program_manager.find_program_imports(&test_program).unwrap();
assert_eq!(imports.len(), 1);
let credits_program_on_chain = &imports[0];
assert_eq!(&credits, credits_program_on_chain);
}
#[test]
fn test_network_resolution_doesnt_find_programs_not_on_chain() {
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let random_program = random_program_id(16);
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
assert!(program_manager.find_program_on_chain(&program_id).is_err())
}
#[test]
fn test_network_resolution_produces_resolution_errors_for_bad_imports() {
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
let imports = program_manager.find_program_imports(&bad_import_program);
assert!(imports.is_err());
}
}