/ vm / package / build.rs
build.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      /// Builds the package.
 20      pub fn build<A: crate::circuit::Alpha<Network = N, BaseField = N::Field>>(&self) -> Result<()> {
 21          // Skip the 'build' if the program is already built.
 22          if !self.is_build_required::<A>() {
 23              return Ok(());
 24          }
 25  
 26          // Retrieve the main program.
 27          let program = self.program();
 28          // Retrieve the program ID.
 29          let program_id = program.id();
 30  
 31          dev_println!("⏳ Compiling '{}'...\n", program_id.to_string());
 32  
 33          // Prepare the build directory.
 34          let build_directory = self.build_directory();
 35          // Create the build directory if it does not exist.
 36          if !build_directory.exists() {
 37              std::fs::create_dir_all(&build_directory)?;
 38          }
 39  
 40          // Construct the process.
 41          let process = self.get_process()?;
 42  
 43          // Synthesize each proving and verifying key.
 44          for function_name in program.functions().keys() {
 45              process.synthesize_key::<A, _>(program_id, function_name, &mut rand::thread_rng())?;
 46          }
 47  
 48          // Load each function circuit.
 49          for function_name in program.functions().keys() {
 50              // Retrieve the program.
 51              let stack = process.get_stack(program_id)?;
 52              let program = stack.program();
 53              // Retrieve the function from the program.
 54              let function = program.get_function(function_name)?;
 55              // Save all the prover and verifier files for any function calls that are made.
 56              for instruction in function.instructions() {
 57                  if let Instruction::Call(call) = instruction {
 58                      // Get the external stack and resource.
 59                      let (external_stack, resource) = match call.operator() {
 60                          CallOperator::Locator(locator) => {
 61                              (Some(process.get_stack(locator.program_id())?), locator.resource())
 62                          }
 63                          CallOperator::Resource(resource) => (None, resource),
 64                      };
 65                      // Retrieve the program.
 66                      let program = match &external_stack {
 67                          Some(external_stack) => external_stack.program(),
 68                          None => program,
 69                      };
 70                      // If this is a function call, save its corresponding prover and verifier files.
 71                      if program.contains_function(resource) {
 72                          // Set the function name to the resource, in this scope.
 73                          let function_name = resource;
 74                          // Retrieve the proving key.
 75                          let proving_key = process.get_proving_key(program.id(), resource)?;
 76                          // Retrieve the verifying key.
 77                          let verifying_key = process.get_verifying_key(program.id(), resource)?;
 78  
 79                          // Prepare the build directory for the imported program.
 80                          let import_build_directory =
 81                              self.build_directory().join(format!("{}-{}", program.id().name(), program.id().network()));
 82                          // Create the build directory if it does not exist.
 83                          if !import_build_directory.exists() {
 84                              std::fs::create_dir_all(&import_build_directory)?;
 85                          }
 86  
 87                          // Create the prover.
 88                          let _prover = ProverFile::create(&import_build_directory, function_name, proving_key)?;
 89                          // Create the verifier.
 90                          let _verifier = VerifierFile::create(&import_build_directory, function_name, verifying_key)?;
 91                      }
 92                  }
 93              }
 94  
 95              // Retrieve the proving key.
 96              let proving_key = process.get_proving_key(program_id, function_name)?;
 97              // Retrieve the verifying key.
 98              let verifying_key = process.get_verifying_key(program_id, function_name)?;
 99  
100              // Create the prover.
101              let _prover = ProverFile::create(&build_directory, function_name, proving_key)?;
102              // Create the verifier.
103              let _verifier = VerifierFile::create(&build_directory, function_name, verifying_key)?;
104          }
105  
106          // Lastly, write the AVM file.
107          let _avm_file = AVMFile::create(&build_directory, program.clone(), true)?;
108  
109          // Ensure the build directory exists.
110          if !self.build_directory().exists() {
111              bail!("Build directory does not exist: {}", self.build_directory().display());
112          }
113  
114          Ok(())
115      }
116  }
117  
118  #[cfg(test)]
119  mod tests {
120      type CurrentAlpha = alphavm_circuit::network::AlphaV0;
121  
122      #[test]
123      fn test_build() {
124          // Samples a new package at a temporary directory.
125          let (directory, package) = crate::package::test_helpers::sample_token_package();
126  
127          // Ensure the build directory does *not* exist.
128          assert!(!package.build_directory().exists());
129          // Build the package.
130          package.build::<CurrentAlpha>().unwrap();
131          // Ensure the build directory exists.
132          assert!(package.build_directory().exists());
133  
134          // Proactively remove the temporary directory (to conserve space).
135          std::fs::remove_dir_all(directory).unwrap();
136      }
137  
138      #[test]
139      fn test_build_with_import() {
140          // Samples a new package at a temporary directory.
141          let (directory, package) = crate::package::test_helpers::sample_wallet_package();
142  
143          // Ensure the build directory does *not* exist.
144          assert!(!package.build_directory().exists());
145          // Build the package.
146          package.build::<CurrentAlpha>().unwrap();
147          // Ensure the build directory exists.
148          assert!(package.build_directory().exists());
149  
150          // Proactively remove the temporary directory (to conserve space).
151          std::fs::remove_dir_all(directory).unwrap();
152      }
153  
154      #[test]
155      #[ignore]
156      fn test_build_with_import_credits() {
157          // Samples a new package at a temporary directory.
158          let (directory, package) = crate::package::test_helpers::sample_transfer_package();
159  
160          // Ensure the build directory does *not* exist.
161          assert!(!package.build_directory().exists());
162          // Build the package.
163          package.build::<CurrentAlpha>().unwrap();
164          // Ensure the build directory exists.
165          assert!(package.build_directory().exists());
166  
167          // Proactively remove the temporary directory (to conserve space).
168          std::fs::remove_dir_all(directory).unwrap();
169      }
170  }