/ vm / package / is_build_required.rs
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  }