aleo_rust/program/
resolver.rs1use super::*;
18
19impl<N: Network> ProgramManager<N> {
20 pub fn find_program(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
22 self.find_program_on_disk(program_id).or_else(|_| self.find_program_on_chain(program_id))
23 }
24
25 pub fn find_program_on_disk(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
27 let local_program_directory =
28 self.local_program_directory.as_ref().ok_or_else(|| anyhow!("Local program directory not set"))?;
29 let imports_directory = local_program_directory.join("imports");
30 ensure!(local_program_directory.exists(), "The program directory does not exist");
32
33 ensure!(!Program::is_reserved_keyword(program_id.name()), "Program name is invalid (reserved): {program_id}");
34
35 ensure!(
36 Manifest::<N>::exists_at(local_program_directory),
37 "Please ensure that the manifest file exists in the Aleo program directory (missing '{}' at '{}')",
38 Manifest::<N>::file_name(),
39 local_program_directory.display()
40 );
41
42 let manifest = Manifest::<N>::open(local_program_directory)?;
44
45 if manifest.program_id() == program_id {
47 let package = Package::open(local_program_directory)?;
49 Ok(package.program().clone())
51 } else {
52 let import_file = imports_directory.join(program_id.to_string());
53 ensure!(
54 import_file.exists(),
55 "No program named {program_id:?} found at {:?}",
56 local_program_directory.display()
57 );
58 println!("Attempting to load program {program_id:?} at {:?}", import_file.display());
59 let mut program_file = File::open(import_file)?;
60 let mut program_string = String::new();
61 program_file.read_to_string(&mut program_string).map_err(|err| anyhow::anyhow!(err.to_string()))?;
62 let program = Program::from_str(&program_string)?;
63 println!("Loaded program {program_id:?} successfully!");
64 Ok(program)
65 }
66 }
67
68 pub fn find_program_on_chain(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
70 self.api_client()?.get_program(program_id)
71 }
72
73 pub fn find_program_imports(&self, program: &Program<N>) -> Result<Vec<Program<N>>> {
75 let mut imports = vec![];
76 for program_id in program.imports().keys() {
77 if let Ok(program) = self.find_program(program_id) {
78 imports.push(program);
79 } else {
80 bail!("Could not find program import: {:?}", program_id);
81 }
82 }
83 Ok(imports)
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::{
91 test_utils::{
92 random_program_id,
93 setup_directory,
94 teardown_directory,
95 HELLO_PROGRAM,
96 IMPORT_PROGRAM,
97 RECIPIENT_PRIVATE_KEY,
98 },
99 AleoAPIClient,
100 };
101 use snarkvm_console::{account::PrivateKey, network::Testnet3};
102
103 use std::{ops::Add, panic::catch_unwind, str::FromStr};
104
105 #[test]
106 fn test_file_loading_and_imports() {
107 let private_key = PrivateKey::<Testnet3>::new(&mut rand::thread_rng()).unwrap();
108 let credits = Program::<Testnet3>::credits().unwrap().to_string();
109 let imports = vec![("credits.aleo", credits.as_str()), ("hello.aleo", HELLO_PROGRAM)];
110 let test_path = setup_directory("aleo_test_file_resolution", IMPORT_PROGRAM, imports).unwrap();
111
112 let result = catch_unwind(|| {
113 let program_manager =
115 ProgramManager::<Testnet3>::new(Some(private_key), None, None, Some(test_path.clone()), false).unwrap();
116
117 let program_id = ProgramID::<Testnet3>::from_str("aleo_test.aleo").unwrap();
119 let expected_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
120 let found_program = program_manager.find_program_on_disk(&program_id).unwrap();
121 assert_eq!(expected_program, found_program);
122
123 let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
125 let credits_program = Program::<Testnet3>::credits().unwrap();
126 let imports = program_manager.find_program_imports(&test_program).unwrap();
127 assert_eq!(imports.len(), 1);
128
129 let local_credits_program = &imports[0];
130 assert_eq!(&credits_program, local_credits_program);
131
132 let random_program = random_program_id(16);
134 let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
135 assert!(program_manager.find_program_on_disk(&program_id).is_err());
136
137 let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
139 let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
140 let imports = program_manager.find_program_imports(&bad_import_program);
141 assert!(imports.is_err());
142
143 let credits = Program::<Testnet3>::credits().unwrap();
145 let imports = program_manager.find_program_imports(&credits).unwrap();
146 assert_eq!(imports.len(), 0);
147 });
148 teardown_directory(&test_path);
149 assert!(!test_path.exists());
151 result.unwrap();
152 }
153
154 #[test]
155 fn test_hybrid_program_and_import_loading() {
156 let credits_program_string = Program::<Testnet3>::credits().unwrap().to_string();
157 let imports = vec![("credits.aleo", credits_program_string.as_str())];
158 let test_path = setup_directory("aleo_test_hybrid_resolution", IMPORT_PROGRAM, imports).unwrap();
159 let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
160
161 let result = catch_unwind(|| {
162 let api_client = AleoAPIClient::<Testnet3>::testnet3();
164 let program_manager = ProgramManager::<Testnet3>::new(
165 Some(private_key),
166 None,
167 Some(api_client),
168 Some(test_path.clone()),
169 false,
170 )
171 .unwrap();
172
173 let program_id = ProgramID::<Testnet3>::from_str("aleo_test.aleo").unwrap();
175 let expected_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
176 let found_program = program_manager.find_program(&program_id).unwrap();
177 assert_eq!(expected_program, found_program);
178
179 let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
181 let credits_program = Program::<Testnet3>::credits().unwrap();
182 let credits_id = credits_program.id();
183 let imports = program_manager.find_program_imports(&test_program).unwrap();
184 assert_eq!(imports.len(), 1);
185
186 let local_credits_program = &imports[0];
187 assert_eq!(&credits_program, local_credits_program);
188
189 let random_program = random_program_id(16);
191 let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
192 assert!(program_manager.find_program(&program_id).is_err());
193
194 assert_eq!(program_manager.find_program(credits_id).unwrap(), credits_program);
196
197 let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
200 let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
201 let imports = program_manager.find_program_imports(&bad_import_program);
202 assert!(imports.is_err());
203
204 let credits = Program::<Testnet3>::credits().unwrap();
207 let imports = program_manager.find_program_imports(&credits).unwrap();
208 assert_eq!(imports.len(), 0);
209 });
210 teardown_directory(&test_path);
211 assert!(!test_path.exists());
213 result.unwrap();
214 }
215
216 #[test]
217 fn test_network_program_resolution() {
218 let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
220 let api_client = AleoAPIClient::<Testnet3>::testnet3();
221 let program_manager =
222 ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
223 let program_id = ProgramID::<Testnet3>::from_str("credits.aleo").unwrap();
224 let credits_off_the_chain = Program::<Testnet3>::credits().unwrap();
225 let credits_on_the_chain = program_manager.find_program_on_chain(&program_id).unwrap();
226 assert_eq!(credits_off_the_chain, credits_on_the_chain);
227 }
228
229 #[test]
230 fn test_network_program_imports_are_resolved_correctly() {
231 let credits = Program::<Testnet3>::credits().unwrap();
232 let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
234 let api_client = AleoAPIClient::<Testnet3>::testnet3();
235 let program_manager =
236 ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
237
238 let test_program = Program::<Testnet3>::from_str(IMPORT_PROGRAM).unwrap();
240 let imports = program_manager.find_program_imports(&test_program).unwrap();
242 assert_eq!(imports.len(), 1);
243
244 let credits_program_on_chain = &imports[0];
245 assert_eq!(&credits, credits_program_on_chain);
247 }
249
250 #[test]
251 fn test_network_resolution_doesnt_find_programs_not_on_chain() {
252 let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
254 let random_program = random_program_id(16);
255 let api_client = AleoAPIClient::<Testnet3>::testnet3();
256
257 let program_manager =
259 ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
260
261 let program_id = ProgramID::<Testnet3>::from_str(&random_program).unwrap();
263 assert!(program_manager.find_program_on_chain(&program_id).is_err())
264 }
265
266 #[test]
267 fn test_network_resolution_produces_resolution_errors_for_bad_imports() {
268 let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
270 let api_client = AleoAPIClient::<Testnet3>::testnet3();
271 let program_manager =
272 ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), None, false).unwrap();
273
274 let bad_import_code = String::from("import ").add(&random_program_id(16)).add(";").add(IMPORT_PROGRAM);
276 let bad_import_program = Program::<Testnet3>::from_str(&bad_import_code).unwrap();
277
278 let imports = program_manager.find_program_imports(&bad_import_program);
280 assert!(imports.is_err());
281 }
282}