Skip to main content

snarkvm/package/
build.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM 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
16use super::*;
17
18impl<N: Network> Package<N> {
19    /// Builds the package.
20    pub fn build<A: crate::circuit::Aleo<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                // Note: `CallDynamic` is not handled here because its targets are resolved at runtime,
58                // so we cannot preload prover/verifier files for dynamic calls at build time.
59                if let Instruction::Call(call) = instruction {
60                    // Get the external stack and resource.
61                    let (external_stack, resource) = match call.operator() {
62                        CallOperator::Locator(locator) => {
63                            (Some(process.get_stack(locator.program_id())?), locator.resource())
64                        }
65                        CallOperator::Resource(resource) => (None, resource),
66                    };
67                    // Retrieve the program.
68                    let program = match &external_stack {
69                        Some(external_stack) => external_stack.program(),
70                        None => program,
71                    };
72                    // If this is a function call, save its corresponding prover and verifier files.
73                    if program.contains_function(resource) {
74                        // Set the function name to the resource, in this scope.
75                        let function_name = resource;
76                        // Retrieve the proving key.
77                        let proving_key = process.get_proving_key(program.id(), resource)?;
78                        // Retrieve the verifying key.
79                        let verifying_key = process.get_verifying_key(program.id(), resource)?;
80
81                        // Prepare the build directory for the imported program.
82                        let import_build_directory =
83                            self.build_directory().join(format!("{}-{}", program.id().name(), program.id().network()));
84                        // Create the build directory if it does not exist.
85                        if !import_build_directory.exists() {
86                            std::fs::create_dir_all(&import_build_directory)?;
87                        }
88
89                        // Create the prover.
90                        let _prover = ProverFile::create(&import_build_directory, function_name, proving_key)?;
91                        // Create the verifier.
92                        let _verifier = VerifierFile::create(&import_build_directory, function_name, verifying_key)?;
93                    }
94                }
95            }
96
97            // Retrieve the proving key.
98            let proving_key = process.get_proving_key(program_id, function_name)?;
99            // Retrieve the verifying key.
100            let verifying_key = process.get_verifying_key(program_id, function_name)?;
101
102            // Create the prover.
103            let _prover = ProverFile::create(&build_directory, function_name, proving_key)?;
104            // Create the verifier.
105            let _verifier = VerifierFile::create(&build_directory, function_name, verifying_key)?;
106        }
107
108        // Lastly, write the AVM file.
109        let _avm_file = AVMFile::create(&build_directory, program.clone(), true)?;
110
111        // Ensure the build directory exists.
112        if !self.build_directory().exists() {
113            bail!("Build directory does not exist: {}", self.build_directory().display());
114        }
115
116        Ok(())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    type CurrentAleo = snarkvm_circuit::network::AleoV0;
123
124    #[test]
125    fn test_build() {
126        // Samples a new package at a temporary directory.
127        let (directory, package) = crate::package::test_helpers::sample_token_package();
128
129        // Ensure the build directory does *not* exist.
130        assert!(!package.build_directory().exists());
131        // Build the package.
132        package.build::<CurrentAleo>().unwrap();
133        // Ensure the build directory exists.
134        assert!(package.build_directory().exists());
135
136        // Proactively remove the temporary directory (to conserve space).
137        std::fs::remove_dir_all(directory).unwrap();
138    }
139
140    #[test]
141    fn test_build_with_import() {
142        // Samples a new package at a temporary directory.
143        let (directory, package) = crate::package::test_helpers::sample_wallet_package();
144
145        // Ensure the build directory does *not* exist.
146        assert!(!package.build_directory().exists());
147        // Build the package.
148        package.build::<CurrentAleo>().unwrap();
149        // Ensure the build directory exists.
150        assert!(package.build_directory().exists());
151
152        // Proactively remove the temporary directory (to conserve space).
153        std::fs::remove_dir_all(directory).unwrap();
154    }
155
156    #[test]
157    #[ignore]
158    fn test_build_with_import_credits() {
159        // Samples a new package at a temporary directory.
160        let (directory, package) = crate::package::test_helpers::sample_transfer_package();
161
162        // Ensure the build directory does *not* exist.
163        assert!(!package.build_directory().exists());
164        // Build the package.
165        package.build::<CurrentAleo>().unwrap();
166        // Ensure the build directory exists.
167        assert!(package.build_directory().exists());
168
169        // Proactively remove the temporary directory (to conserve space).
170        std::fs::remove_dir_all(directory).unwrap();
171    }
172}