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 }