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