snarkvm_synthesizer_process/stack/
execute.rs

1// Copyright (c) 2019-2025 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> Stack<N> {
19    /// Executes a program closure on the given inputs.
20    ///
21    /// # Errors
22    /// This method will halt if the given inputs are not the same length as the input statements.
23    pub fn execute_closure<A: circuit::Aleo<Network = N>>(
24        &self,
25        closure: &Closure<N>,
26        inputs: &[circuit::Value<A>],
27        call_stack: CallStack<N>,
28        signer: circuit::Address<A>,
29        caller: circuit::Address<A>,
30        tvk: circuit::Field<A>,
31    ) -> Result<Vec<circuit::Value<A>>> {
32        let timer = timer!("Stack::execute_closure");
33
34        // Ensure the call stack is not `Evaluate`.
35        ensure!(!matches!(call_stack, CallStack::Evaluate(..)), "Illegal operation: cannot evaluate in execute mode");
36
37        // Ensure the number of inputs matches the number of input statements.
38        if closure.inputs().len() != inputs.len() {
39            bail!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len())
40        }
41        lap!(timer, "Check the number of inputs");
42
43        // Retrieve the number of public variables in the circuit.
44        let num_public = A::num_public();
45
46        // Initialize the registers.
47        let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone());
48        // Set the transition signer, as a circuit.
49        registers.set_signer_circuit(signer);
50        // Set the transition caller, as a circuit.
51        registers.set_caller_circuit(caller);
52        // Set the transition view key, as a circuit.
53        registers.set_tvk_circuit(tvk);
54        lap!(timer, "Initialize the registers");
55
56        // Store the inputs.
57        closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
58            // If the circuit is in execute mode, then store the console input.
59            if let CallStack::Execute(..) = registers.call_stack_ref() {
60                use circuit::Eject;
61                // Assign the console input to the register.
62                registers.store(self, register, input.eject_value())?;
63            }
64            // Assign the circuit input to the register.
65            registers.store_circuit(self, register, input.clone())
66        })?;
67        lap!(timer, "Store the inputs");
68
69        // Execute the instructions.
70        for instruction in closure.instructions() {
71            // If the circuit is in execute mode, then evaluate the instructions.
72            if let CallStack::Execute(..) = registers.call_stack_ref() {
73                // If the evaluation fails, bail and return the error.
74                if let Err(error) = instruction.evaluate(self, &mut registers) {
75                    bail!("Failed to evaluate instruction ({instruction}): {error}");
76                }
77            }
78            // Execute the instruction.
79            instruction.execute(self, &mut registers)?;
80        }
81        lap!(timer, "Execute the instructions");
82
83        // Ensure the number of public variables remains the same.
84        ensure!(A::num_public() == num_public, "Illegal closure operation: instructions injected public variables");
85
86        use circuit::Inject;
87
88        // Load the outputs.
89        let outputs = closure
90            .outputs()
91            .iter()
92            .map(|output| {
93                match output.operand() {
94                    // If the operand is a literal, use the literal directly.
95                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
96                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
97                    ))),
98                    // If the operand is a register, retrieve the stack value from the register.
99                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
100                    // If the operand is the program ID, convert the program ID into an address.
101                    Operand::ProgramID(program_id) => {
102                        Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
103                            circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
104                        ))))
105                    }
106                    // If the operand is the signer, retrieve the signer from the registers.
107                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
108                        circuit::Literal::Address(registers.signer_circuit()?),
109                    ))),
110                    // If the operand is the caller, retrieve the caller from the registers.
111                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
112                        circuit::Literal::Address(registers.caller_circuit()?),
113                    ))),
114                    // If the operand is the block height, throw an error.
115                    Operand::BlockHeight => {
116                        bail!("Illegal operation: cannot retrieve the block height in a closure scope")
117                    }
118                    // If the operand is the network id, throw an error.
119                    Operand::NetworkID => {
120                        bail!("Illegal operation: cannot retrieve the network id in a closure scope")
121                    }
122                    // If the operand is the checksum, throw an error.
123                    Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"),
124                    // If the operand is the edition, throw an error.
125                    Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"),
126                    // If the operand is the program owner, throw an error.
127                    Operand::ProgramOwner(_) => {
128                        bail!("Illegal operation: cannot retrieve the program owner in a closure scope")
129                    }
130                }
131            })
132            .collect();
133        lap!(timer, "Load the outputs");
134
135        finish!(timer);
136        outputs
137    }
138
139    /// Executes a program function on the given inputs.
140    ///
141    /// Note: To execute a transition, do **not** call this method. Instead, call `Process::execute`.
142    ///
143    /// # Errors
144    /// This method will halt if the given inputs are not the same length as the input statements.
145    pub fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
146        &self,
147        mut call_stack: CallStack<N>,
148        console_caller: Option<ProgramID<N>>,
149        console_root_tvk: Option<Field<N>>,
150        rng: &mut R,
151    ) -> Result<Response<N>> {
152        let timer = timer!("Stack::execute_function");
153
154        // Ensure the global constants for the Aleo environment are initialized.
155        A::initialize_global_constants();
156        // Ensure the circuit environment is clean.
157        A::reset();
158
159        // If in 'CheckDeployment' mode, set the constraint limit and variable limit.
160        // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those.
161        if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack {
162            A::set_constraint_limit(*constraint_limit);
163            A::set_variable_limit(*variable_limit);
164        }
165
166        // Retrieve the next request.
167        let console_request = call_stack.pop()?;
168
169        // Ensure the network ID matches.
170        ensure!(
171            **console_request.network_id() == N::ID,
172            "Network ID mismatch. Expected {}, but found {}",
173            N::ID,
174            console_request.network_id()
175        );
176
177        // We can only have a root_tvk if this request was called by another request
178        ensure!(console_caller.is_some() == console_root_tvk.is_some());
179        // Determine if this is the top-level caller.
180        let console_is_root = console_caller.is_none();
181
182        // Determine the parent.
183        //  - If this execution is the top-level caller, then the parent is the program ID.
184        //  - If this execution is a child caller, then the parent is the caller.
185        let console_parent = match console_caller {
186            // If this execution is the top-level caller, then the parent is the program ID.
187            None => console_request.program_id().to_address()?,
188            // If this execution is a child caller, then the parent is the caller.
189            Some(console_caller) => console_caller.to_address()?,
190        };
191
192        // Retrieve the function from the program.
193        let function = self.get_function(console_request.function_name())?;
194        // Retrieve the number of inputs.
195        let num_inputs = function.inputs().len();
196        // Ensure the number of inputs matches the number of input statements.
197        if num_inputs != console_request.inputs().len() {
198            bail!("Expected {num_inputs} inputs, found {}", console_request.inputs().len())
199        }
200        // Retrieve the input types.
201        let input_types = function.input_types();
202        // Retrieve the output types.
203        let output_types = function.output_types();
204        lap!(timer, "Retrieve the input and output types");
205
206        // Ensure the inputs match their expected types.
207        console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
208            // Ensure the input matches the input type in the function.
209            self.matches_value_type(input, input_type)
210        })?;
211        lap!(timer, "Verify the input types");
212
213        // Retrieve the program checksum, if the program has a constructor.
214        let program_checksum = match self.program().contains_constructor() {
215            true => Some(self.program_checksum_as_field()?),
216            false => None,
217        };
218
219        // Ensure the request is well-formed.
220        ensure!(
221            console_request.verify(&input_types, console_is_root, program_checksum),
222            "[Execute] Request is invalid"
223        );
224        lap!(timer, "Verify the console request");
225
226        // Initialize the registers.
227        let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
228
229        // Set the root tvk, from a parent request or the current request.
230        let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk());
231        // Inject the `root_tvk` as `Mode::Private`.
232        let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk);
233        // Set the root tvk.
234        registers.set_root_tvk(console_root_tvk);
235        // Set the root tvk, as a circuit.
236        registers.set_root_tvk_circuit(root_tvk.clone());
237
238        // If a program checksum was passed in, Inject it as `Mode::Public`.
239        let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c));
240
241        use circuit::{Eject, Inject};
242
243        // Inject the transition public key `tpk` as `Mode::Public`.
244        let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
245        // Inject the request as `Mode::Private`.
246        let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
247
248        // Inject `is_root` as `Mode::Public`.
249        let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
250        // Inject the parent as `Mode::Public`.
251        let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
252        // Determine the caller.
253        let caller = Ternary::ternary(&is_root, request.signer(), &parent);
254
255        // Ensure the request has a valid signature, inputs, and transition view key.
256        A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum));
257        lap!(timer, "Verify the circuit request");
258
259        // Set the transition signer.
260        registers.set_signer(*console_request.signer());
261        // Set the transition signer, as a circuit.
262        registers.set_signer_circuit(request.signer().clone());
263
264        // Set the transition caller.
265        registers.set_caller(caller.eject_value());
266        // Set the transition caller, as a circuit.
267        registers.set_caller_circuit(caller);
268
269        // Set the transition view key.
270        registers.set_tvk(*console_request.tvk());
271        // Set the transition view key, as a circuit.
272        registers.set_tvk_circuit(request.tvk().clone());
273
274        lap!(timer, "Initialize the registers");
275
276        Self::log_circuit::<A>("Request");
277
278        // Retrieve the number of constraints for verifying the request in the circuit.
279        let num_request_constraints = A::num_constraints();
280
281        // Retrieve the number of public variables in the circuit.
282        let num_public = A::num_public();
283
284        // Store the inputs.
285        function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
286            // If the circuit is in execute mode, then store the console input.
287            if let CallStack::Execute(..) = registers.call_stack_ref() {
288                // Assign the console input to the register.
289                registers.store(self, register, input.eject_value())?;
290            }
291            // Assign the circuit input to the register.
292            registers.store_circuit(self, register, input.clone())
293        })?;
294        lap!(timer, "Store the inputs");
295
296        // Initialize a tracker to determine if there are any function calls.
297        let mut contains_function_call = false;
298
299        // Execute the instructions.
300        for instruction in function.instructions() {
301            // If the circuit is in execute mode, then evaluate the instructions.
302            if let CallStack::Execute(..) = registers.call_stack_ref() {
303                // Evaluate the instruction.
304                let result = match instruction {
305                    // If the instruction is a `call` instruction, we need to handle it separately.
306                    Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng),
307                    // Otherwise, evaluate the instruction normally.
308                    _ => instruction.evaluate(self, &mut registers),
309                };
310                // If the evaluation fails, bail and return the error.
311                if let Err(error) = result {
312                    bail!("Failed to evaluate instruction ({instruction}): {error}");
313                }
314            }
315
316            // Execute the instruction.
317            let result = match instruction {
318                // If the instruction is a `call` instruction, we need to handle it separately.
319                Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng),
320                // Otherwise, execute the instruction normally.
321                _ => instruction.execute(self, &mut registers),
322            };
323            // If the execution fails, bail and return the error.
324            if let Err(error) = result {
325                bail!("Failed to execute instruction ({instruction}): {error}");
326            }
327
328            // If the instruction was a function call, then set the tracker to `true`.
329            if let Instruction::Call(call) = instruction {
330                // Check if the call is a function call.
331                if call.is_function_call(self)? {
332                    contains_function_call = true;
333                }
334            }
335        }
336        lap!(timer, "Execute the instructions");
337
338        // Load the outputs.
339        let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
340        let outputs = output_operands
341            .iter()
342            .map(|operand| {
343                match operand {
344                    // If the operand is a literal, use the literal directly.
345                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
346                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
347                    ))),
348                    // If the operand is a register, retrieve the stack value from the register.
349                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
350                    // If the operand is the program ID, convert the program ID into an address.
351                    Operand::ProgramID(program_id) => {
352                        Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
353                            circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
354                        ))))
355                    }
356                    // If the operand is the signer, retrieve the signer from the registers.
357                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
358                        circuit::Literal::Address(registers.signer_circuit()?),
359                    ))),
360                    // If the operand is the caller, retrieve the caller from the registers.
361                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
362                        circuit::Literal::Address(registers.caller_circuit()?),
363                    ))),
364                    // If the operand is the block height, throw an error.
365                    Operand::BlockHeight => {
366                        bail!("Illegal operation: cannot retrieve the block height in a function scope")
367                    }
368                    // If the operand is the network id, throw an error.
369                    Operand::NetworkID => {
370                        bail!("Illegal operation: cannot retrieve the network id in a function scope")
371                    }
372                    // If the operand is the checksum, throw an error.
373                    Operand::Checksum(_) => {
374                        bail!("Illegal operation: cannot retrieve the checksum in a function scope")
375                    }
376                    // If the operand is the edition, throw an error.
377                    Operand::Edition(_) => {
378                        bail!("Illegal operation: cannot retrieve the edition in a function scope")
379                    }
380                    // If the operand is the program owner, throw an error.
381                    Operand::ProgramOwner(_) => {
382                        bail!("Illegal operation: cannot retrieve the program owner in a function scope")
383                    }
384                }
385            })
386            .collect::<Result<Vec<_>>>()?;
387        lap!(timer, "Load the outputs");
388
389        // Map the output operands into registers.
390        let output_registers = output_operands
391            .iter()
392            .map(|operand| match operand {
393                Operand::Register(register) => Some(register.clone()),
394                _ => None,
395            })
396            .collect::<Vec<_>>();
397
398        Self::log_circuit::<A>(format!("Function '{}()'", function.name()));
399
400        // Retrieve the number of constraints for executing the function in the circuit.
401        let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
402
403        // If the function does not contain function calls, ensure no new public variables were injected.
404        if !contains_function_call {
405            // Ensure the number of public variables remains the same.
406            ensure!(A::num_public() == num_public, "Instructions in function injected public variables");
407        }
408
409        // Construct the response.
410        let response = circuit::Response::from_outputs(
411            request.signer(),
412            request.network_id(),
413            request.program_id(),
414            request.function_name(),
415            num_inputs,
416            request.tvk(),
417            request.tcm(),
418            outputs,
419            &output_types,
420            &output_registers,
421        );
422        lap!(timer, "Construct the response");
423
424        Self::log_circuit::<A>("Response");
425
426        // Retrieve the number of constraints for verifying the response in the circuit.
427        let num_response_constraints =
428            A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints);
429
430        Self::log_circuit::<A>("Complete");
431
432        // Eject the response.
433        let response = response.eject_value();
434
435        // Ensure the outputs matches the expected value types.
436        response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
437            // Ensure the output matches its expected type.
438            self.matches_value_type(output, output_type)
439        })?;
440
441        // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied.
442        if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
443            // If the circuit is empty or not satisfied, then throw an error.
444            ensure!(
445                A::num_constraints() > 0 && A::is_satisfied(),
446                "'{}/{}' is not satisfied on the given inputs ({} constraints).",
447                self.program.id(),
448                function.name(),
449                A::num_constraints()
450            );
451        }
452
453        // Eject the circuit assignment and reset the circuit.
454        let assignment = A::eject_assignment_and_reset();
455
456        // If the circuit is in `Synthesize` or `Execute` mode, synthesize the circuit key, if it does not exist.
457        if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
458            // If the proving key does not exist, then synthesize it.
459            if !self.contains_proving_key(function.name()) {
460                // Add the circuit key to the mapping.
461                self.synthesize_from_assignment(function.name(), &assignment)?;
462                lap!(timer, "Synthesize the {} circuit key", function.name());
463            }
464        }
465        // If the circuit is in `Authorize` mode, then save the transition.
466        if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
467            // Construct the transition.
468            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
469            // Add the transition to the authorization.
470            authorization.insert_transition(transition)?;
471            lap!(timer, "Save the transition");
472        }
473        // If the circuit is in `CheckDeployment` mode, then save the assignment.
474        else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() {
475            // Construct the call metrics.
476            let metrics = CallMetrics {
477                program_id: *self.program_id(),
478                function_name: *function.name(),
479                num_instructions: function.instructions().len(),
480                num_request_constraints,
481                num_function_constraints,
482                num_response_constraints,
483            };
484            // Add the assignment to the assignments.
485            assignments.write().push((assignment, metrics));
486            lap!(timer, "Save the circuit assignment");
487        }
488        // If the circuit is in `Execute` mode, then execute the circuit into a transition.
489        else if let CallStack::Execute(_, trace) = registers.call_stack_ref() {
490            registers.ensure_console_and_circuit_registers_match()?;
491
492            // Construct the transition.
493            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
494
495            // Retrieve the proving key.
496            let proving_key = self.get_proving_key(function.name())?;
497            // Construct the call metrics.
498            let metrics = CallMetrics {
499                program_id: *self.program_id(),
500                function_name: *function.name(),
501                num_instructions: function.instructions().len(),
502                num_request_constraints,
503                num_function_constraints,
504                num_response_constraints,
505            };
506
507            // Add the transition to the trace.
508            trace.write().insert_transition(
509                console_request.input_ids(),
510                &transition,
511                (proving_key, assignment),
512                metrics,
513            )?;
514        }
515        // If the circuit is in `PackageRun` mode, then save the assignment.
516        else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() {
517            // Construct the call metrics.
518            let metrics = CallMetrics {
519                program_id: *self.program_id(),
520                function_name: *function.name(),
521                num_instructions: function.instructions().len(),
522                num_request_constraints,
523                num_function_constraints,
524                num_response_constraints,
525            };
526            // Add the assignment to the assignments.
527            assignments.write().push((assignment, metrics));
528            lap!(timer, "Save the circuit assignment");
529        }
530
531        finish!(timer);
532
533        // Return the response.
534        Ok(response)
535    }
536}
537
538impl<N: Network> Stack<N> {
539    /// Prints the current state of the circuit.
540    #[allow(unused_variables)]
541    pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>>(scope: impl std::fmt::Display) {
542        #[cfg(debug_assertions)]
543        {
544            use snarkvm_utilities::dev_println;
545
546            use colored::Colorize as _;
547
548            // Determine if the circuit is satisfied.
549            let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" };
550            // Determine the count.
551            let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
552
553            let scope = scope.to_string().bold();
554
555            // Print the log.
556            dev_println!(
557                "{is_satisfied} {scope:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})",
558                width = 20
559            );
560        }
561    }
562}