is_build_required.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use super::*; 17 18 impl<N: Network> Package<N> { 19 /// Returns `true` if the package is stale or has not been built. 20 pub fn is_build_required<A: crate::circuit::Alpha<Network = N, BaseField = N::Field>>(&self) -> bool { 21 // Prepare the build directory. 22 let build_directory = self.build_directory(); 23 // If the build directory does not exist, then a build is required. 24 if !build_directory.exists() { 25 return true; 26 } 27 28 // If the main AVM file does not exists, then a build is required. 29 if !AVMFile::<N>::main_exists_at(&build_directory) { 30 return true; 31 } 32 33 // Open the main AVM file. 34 let avm_file = match AVMFile::open(&build_directory, &self.program_id, true) { 35 // Retrieve the main AVM file. 36 Ok(file) => file, 37 // If the main AVM file fails to open, then a build is required. 38 Err(_) => return true, 39 }; 40 41 // Check if the main program matches. 42 let program = self.program(); 43 if avm_file.program() != program { 44 return true; 45 } 46 47 // Next, check if the prover and verifier exist for each function. 48 for function_name in program.functions().keys() { 49 // Check if the prover file exists. 50 if !ProverFile::exists_at(&build_directory, function_name) { 51 // If not, we need to build the circuit. 52 return true; 53 } 54 // Check if the verifier file exists. 55 if !VerifierFile::exists_at(&build_directory, function_name) { 56 // If not, we need to build the circuit. 57 return true; 58 } 59 } 60 61 // Skip building the package, as it has not changed. 62 false 63 } 64 } 65 66 #[cfg(test)] 67 mod tests { 68 use super::*; 69 use alphavm_console::network::MainnetV0; 70 use std::{fs::File, io::Write}; 71 72 type CurrentNetwork = MainnetV0; 73 type Alpha = crate::circuit::AlphaV0; 74 75 fn temp_dir() -> PathBuf { 76 tempfile::tempdir().expect("Failed to open temporary directory").keep() 77 } 78 79 fn initialize_unbuilt_package(valid: bool) -> Result<Package<MainnetV0>> { 80 // Initialize a temporary directory. 81 let directory = temp_dir(); 82 83 let program_id = ProgramID::<CurrentNetwork>::from_str("token.alpha").unwrap(); 84 85 let program_string = match valid { 86 true => program_with_id("token.alpha"), 87 false => program_with_id("invalid_id.alpha"), 88 }; 89 90 // Write the program string to a file in the temporary directory. 91 let path = directory.join("main.alpha"); 92 let mut file = File::create(path).unwrap(); 93 file.write_all(program_string.as_bytes()).unwrap(); 94 95 // Create the manifest file. 96 let _manifest_file = Manifest::create(&directory, &program_id).unwrap(); 97 98 // Create the build directory. 99 let build_directory = directory.join("build"); 100 std::fs::create_dir_all(build_directory).unwrap(); 101 102 // Open the package at the temporary directory. 103 Package::<MainnetV0>::open(&directory) 104 } 105 106 fn program_with_id(id: &str) -> String { 107 format!( 108 r"program {id}; 109 110 record token: 111 owner as address.private; 112 token_amount as u64.private; 113 114 function compute: 115 input r0 as token.record; 116 add.w r0.token_amount r0.token_amount into r1; 117 output r1 as u64.private;" 118 ) 119 } 120 121 #[test] 122 fn test_for_new_package() { 123 let package = initialize_unbuilt_package(true).unwrap(); 124 assert!(package.is_build_required::<Alpha>()); 125 } 126 127 #[test] 128 fn test_when_avm_file_does_not_exist() { 129 let package = initialize_unbuilt_package(true).unwrap(); 130 assert!(package.build_directory().exists()); 131 assert!(!AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory())); 132 assert!(package.is_build_required::<Alpha>()); 133 } 134 135 #[test] 136 fn test_fails_when_avm_and_package_program_ids_do_not_match() { 137 let package = initialize_unbuilt_package(false); 138 assert!(package.is_err()); 139 } 140 141 #[test] 142 fn test_when_prover_and_verifier_files_do_not_exist() { 143 let package = initialize_unbuilt_package(true).unwrap(); 144 assert!(package.build_directory().exists()); 145 146 assert!(!AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory())); 147 assert!(AVMFile::<CurrentNetwork>::create(&package.build_directory(), package.program().clone(), true).is_ok()); 148 assert!(AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory())); 149 150 let avm_file = AVMFile::open(&package.build_directory(), &package.program_id, true).unwrap(); 151 assert_eq!(avm_file.program().id(), &package.program_id); 152 assert_eq!(avm_file.program(), package.program()); 153 154 assert!( 155 package 156 .program() 157 .functions() 158 .keys() 159 .filter(|k| { 160 ProverFile::exists_at(&package.build_directory(), k) 161 || VerifierFile::exists_at(&package.build_directory(), k) 162 }) 163 .peekable() 164 .peek() 165 .is_none() 166 ); 167 168 assert!(package.is_build_required::<Alpha>()); 169 } 170 171 #[test] 172 fn test_prebuilt_package_does_not_rebuild() { 173 let package = initialize_unbuilt_package(true).unwrap(); 174 assert!(package.is_build_required::<Alpha>()); 175 176 package.build::<Alpha>().unwrap(); 177 assert!(!package.is_build_required::<Alpha>()); 178 } 179 }