/ vm / package / is_build_required.rs
is_build_required.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::*;
 20  
 21  impl<N: Network> Package<N> {
 22      /// Returns `true` if the package is stale or has not been built.
 23      pub fn is_build_required<A: crate::circuit::Alpha<Network = N, BaseField = N::Field>>(&self) -> bool {
 24          // Prepare the build directory.
 25          let build_directory = self.build_directory();
 26          // If the build directory does not exist, then a build is required.
 27          if !build_directory.exists() {
 28              return true;
 29          }
 30  
 31          // If the main AVM file does not exists, then a build is required.
 32          if !AVMFile::<N>::main_exists_at(&build_directory) {
 33              return true;
 34          }
 35  
 36          // Open the main AVM file.
 37          let avm_file = match AVMFile::open(&build_directory, &self.program_id, true) {
 38              // Retrieve the main AVM file.
 39              Ok(file) => file,
 40              // If the main AVM file fails to open, then a build is required.
 41              Err(_) => return true,
 42          };
 43  
 44          // Check if the main program matches.
 45          let program = self.program();
 46          if avm_file.program() != program {
 47              return true;
 48          }
 49  
 50          // Next, check if the prover and verifier exist for each function.
 51          for function_name in program.functions().keys() {
 52              // Check if the prover file exists.
 53              if !ProverFile::exists_at(&build_directory, function_name) {
 54                  // If not, we need to build the circuit.
 55                  return true;
 56              }
 57              // Check if the verifier file exists.
 58              if !VerifierFile::exists_at(&build_directory, function_name) {
 59                  // If not, we need to build the circuit.
 60                  return true;
 61              }
 62          }
 63  
 64          // Skip building the package, as it has not changed.
 65          false
 66      }
 67  }
 68  
 69  #[cfg(test)]
 70  mod tests {
 71      use super::*;
 72      use alphavm_console::network::MainnetV0;
 73      use std::{fs::File, io::Write};
 74  
 75      type CurrentNetwork = MainnetV0;
 76      type Alpha = crate::circuit::AlphaV0;
 77  
 78      fn temp_dir() -> PathBuf {
 79          tempfile::tempdir().expect("Failed to open temporary directory").keep()
 80      }
 81  
 82      fn initialize_unbuilt_package(valid: bool) -> Result<Package<MainnetV0>> {
 83          // Initialize a temporary directory.
 84          let directory = temp_dir();
 85  
 86          let program_id = ProgramID::<CurrentNetwork>::from_str("token.alpha").unwrap();
 87  
 88          let program_string = match valid {
 89              true => program_with_id("token.alpha"),
 90              false => program_with_id("invalid_id.alpha"),
 91          };
 92  
 93          // Write the program string to a file in the temporary directory.
 94          let path = directory.join("main.alpha");
 95          let mut file = File::create(path).unwrap();
 96          file.write_all(program_string.as_bytes()).unwrap();
 97  
 98          // Create the manifest file.
 99          let _manifest_file = Manifest::create(&directory, &program_id).unwrap();
100  
101          // Create the build directory.
102          let build_directory = directory.join("build");
103          std::fs::create_dir_all(build_directory).unwrap();
104  
105          // Open the package at the temporary directory.
106          Package::<MainnetV0>::open(&directory)
107      }
108  
109      fn program_with_id(id: &str) -> String {
110          format!(
111              r"program {id};
112  
113  record token:
114      owner as address.private;
115      token_amount as u64.private;
116  
117  function compute:
118      input r0 as token.record;
119      add.w r0.token_amount r0.token_amount into r1;
120      output r1 as u64.private;"
121          )
122      }
123  
124      #[test]
125      fn test_for_new_package() {
126          let package = initialize_unbuilt_package(true).unwrap();
127          assert!(package.is_build_required::<Alpha>());
128      }
129  
130      #[test]
131      fn test_when_avm_file_does_not_exist() {
132          let package = initialize_unbuilt_package(true).unwrap();
133          assert!(package.build_directory().exists());
134          assert!(!AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory()));
135          assert!(package.is_build_required::<Alpha>());
136      }
137  
138      #[test]
139      fn test_fails_when_avm_and_package_program_ids_do_not_match() {
140          let package = initialize_unbuilt_package(false);
141          assert!(package.is_err());
142      }
143  
144      #[test]
145      fn test_when_prover_and_verifier_files_do_not_exist() {
146          let package = initialize_unbuilt_package(true).unwrap();
147          assert!(package.build_directory().exists());
148  
149          assert!(!AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory()));
150          assert!(AVMFile::<CurrentNetwork>::create(&package.build_directory(), package.program().clone(), true).is_ok());
151          assert!(AVMFile::<CurrentNetwork>::main_exists_at(&package.build_directory()));
152  
153          let avm_file = AVMFile::open(&package.build_directory(), &package.program_id, true).unwrap();
154          assert_eq!(avm_file.program().id(), &package.program_id);
155          assert_eq!(avm_file.program(), package.program());
156  
157          assert!(package
158              .program()
159              .functions()
160              .keys()
161              .filter(|k| {
162                  ProverFile::exists_at(&package.build_directory(), k)
163                      || VerifierFile::exists_at(&package.build_directory(), k)
164              })
165              .peekable()
166              .peek()
167              .is_none());
168  
169          assert!(package.is_build_required::<Alpha>());
170      }
171  
172      #[test]
173      fn test_prebuilt_package_does_not_rebuild() {
174          let package = initialize_unbuilt_package(true).unwrap();
175          assert!(package.is_build_required::<Alpha>());
176  
177          package.build::<Alpha>().unwrap();
178          assert!(!package.is_build_required::<Alpha>());
179      }
180  }