snarkvm_debug/package/
execute.rs

1// Copyright (C) 2019-2023 Aleo Systems 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// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17impl<N: Network> Package<N> {
18    /// Executes a program function with the given inputs.
19    #[allow(clippy::type_complexity)]
20    pub fn execute<A: crate::circuit::Aleo<Network = N, BaseField = N::Field>, R: Rng + CryptoRng>(
21        &self,
22        endpoint: String,
23        private_key: &PrivateKey<N>,
24        function_name: Identifier<N>,
25        inputs: &[Value<N>],
26        rng: &mut R,
27    ) -> Result<(Response<N>, Execution<N>, Vec<CallMetrics<N>>)> {
28        // Retrieve the main program.
29        let program = self.program();
30        // Retrieve the program ID.
31        let program_id = program.id();
32        // Ensure that the function exists.
33        if !program.contains_function(&function_name) {
34            bail!("Function '{function_name}' does not exist.")
35        }
36
37        // Build the package, if the package requires building.
38        // TODO (howardwu): We currently choose only to support local synthesis of keys due to performance.
39        // self.build::<A>(Some(endpoint.clone()))?;
40        self.build::<A>(None)?;
41
42        // Prepare the locator (even if logging is disabled, to sanity check the locator is well-formed).
43        let locator = Locator::<N>::from_str(&format!("{program_id}/{function_name}"))?;
44
45        #[cfg(feature = "aleo-cli")]
46        println!("🚀 Executing '{}'...\n", locator.to_string().bold());
47
48        // Construct the process.
49        let process = self.get_process()?;
50        // Authorize the function call.
51        let authorization = process.authorize::<A, R>(private_key, program_id, function_name, inputs.iter(), rng)?;
52
53        // Retrieve the program.
54        let program = process.get_program(program_id)?;
55        // Retrieve the function from the program.
56        let function = program.get_function(&function_name)?;
57        // Save all the prover and verifier files for any function calls that are made.
58        for instruction in function.instructions() {
59            if let Instruction::Call(call) = instruction {
60                // Retrieve the program and resource.
61                let (program, resource) = match call.operator() {
62                    CallOperator::Locator(locator) => (process.get_program(locator.program_id())?, locator.resource()),
63                    CallOperator::Resource(resource) => (program, resource),
64                };
65                // If this is a function call, save its corresponding prover and verifier files.
66                if program.contains_function(resource) {
67                    // Set the function name to the resource, in this scope.
68                    let function_name = resource;
69                    // Prepare the build directory for the imported program.
70                    let import_build_directory =
71                        self.build_directory().join(format!("{}-{}", program.id().name(), program.id().network()));
72
73                    // Create the prover.
74                    let prover = ProverFile::open(&import_build_directory, function_name)?;
75                    // Adds the proving key to the process.
76                    process.insert_proving_key(program.id(), function_name, prover.proving_key().clone())?;
77
78                    // Create the verifier.
79                    let verifier = VerifierFile::open(&import_build_directory, function_name)?;
80                    // Adds the verifying key to the process.
81                    process.insert_verifying_key(program.id(), function_name, verifier.verifying_key().clone())?;
82                }
83            }
84        }
85
86        // Prepare the build directory.
87        let build_directory = self.build_directory();
88        // Load the prover.
89        let prover = ProverFile::open(&build_directory, &function_name)?;
90        // Load the verifier.
91        let verifier = VerifierFile::open(&build_directory, &function_name)?;
92
93        // Adds the proving key to the process.
94        process.insert_proving_key(program_id, &function_name, prover.proving_key().clone())?;
95        // Adds the verifying key to the process.
96        process.insert_verifying_key(program_id, &function_name, verifier.verifying_key().clone())?;
97
98        // Execute the circuit.
99        let (response, mut trace) = process.execute::<A, R>(authorization, rng)?;
100
101        // Retrieve the call metrics.
102        let call_metrics = trace.call_metrics().to_vec();
103
104        // Prepare the trace.
105        trace.prepare(Query::<_, BlockMemory<_>>::from(endpoint))?;
106        // Prove the execution.
107        let execution = trace.prove_execution::<A, R>(&locator.to_string(), rng)?;
108        // Return the response, execution, and call metrics.
109        Ok((response, execution, call_metrics))
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use snarkvm_utilities::TestRng;
117
118    type CurrentAleo = snarkvm_circuit::network::AleoV0;
119
120    // TODO: Re-enable this test after `staging` is merged into `testnet3` for the October 18, 2023 calibration reset.
121    #[test]
122    #[ignore]
123    fn test_execute() {
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::<CurrentAleo>(None).unwrap();
131        // Ensure the build directory exists.
132        assert!(package.build_directory().exists());
133
134        // Initialize an RNG.
135        let rng = &mut TestRng::default();
136        // Sample the function inputs.
137        let (private_key, function_name, inputs) =
138            crate::package::test_helpers::sample_package_run(package.program_id());
139        // Construct the endpoint.
140        let endpoint = "https://api.explorer.aleo.org/v1".to_string();
141        // Run the program function.
142        let (_response, _execution, _metrics) =
143            package.execute::<CurrentAleo, _>(endpoint, &private_key, function_name, &inputs, rng).unwrap();
144
145        // Proactively remove the temporary directory (to conserve space).
146        std::fs::remove_dir_all(directory).unwrap();
147    }
148
149    // TODO: Re-enable this test using a mock API endpoint for the `Query` struct.
150    #[test]
151    #[ignore]
152    fn test_execute_with_import() {
153        // Samples a new package at a temporary directory.
154        let (directory, package) = crate::package::test_helpers::sample_wallet_package();
155
156        // Ensure the build directory does *not* exist.
157        assert!(!package.build_directory().exists());
158        // Build the package.
159        package.build::<CurrentAleo>(None).unwrap();
160        // Ensure the build directory exists.
161        assert!(package.build_directory().exists());
162
163        // Initialize an RNG.
164        let rng = &mut TestRng::default();
165        // Sample the function inputs.
166        let (private_key, function_name, inputs) =
167            crate::package::test_helpers::sample_package_run(package.program_id());
168        // Construct the endpoint.
169        let endpoint = "https://api.explorer.aleo.org/v1".to_string();
170        // Run the program function.
171        let (_response, _execution, _metrics) =
172            package.execute::<CurrentAleo, _>(endpoint, &private_key, function_name, &inputs, rng).unwrap();
173
174        // Proactively remove the temporary directory (to conserve space).
175        std::fs::remove_dir_all(directory).unwrap();
176    }
177
178    /// Use `cargo test profiler --features timer` to run this test.
179    #[ignore]
180    #[test]
181    fn test_profiler() -> Result<()> {
182        // Samples a new package at a temporary directory.
183        let (directory, package) = crate::package::test_helpers::sample_token_package();
184
185        // Ensure the build directory does *not* exist.
186        assert!(!package.build_directory().exists());
187        // Build the package.
188        package.build::<CurrentAleo>(None).unwrap();
189        // Ensure the build directory exists.
190        assert!(package.build_directory().exists());
191
192        // Initialize an RNG.
193        let rng = &mut TestRng::default();
194        // Sample the function inputs.
195        let (private_key, function_name, inputs) =
196            crate::package::test_helpers::sample_package_run(package.program_id());
197        // Construct the endpoint.
198        let endpoint = "https://api.explorer.aleo.org/v1".to_string();
199        // Run the program function.
200        let (_response, _execution, _metrics) =
201            package.execute::<CurrentAleo, _>(endpoint, &private_key, function_name, &inputs, rng).unwrap();
202
203        // Proactively remove the temporary directory (to conserve space).
204        std::fs::remove_dir_all(directory).unwrap();
205
206        bail!("\n\nRemember to #[ignore] this test!\n\n")
207    }
208}