Skip to main content

snarkvm_synthesizer_process/stack/
mod.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
16mod authorization;
17pub use authorization::*;
18
19mod call;
20pub use call::*;
21
22mod finalize_registers;
23pub use finalize_registers::*;
24
25mod finalize_types;
26pub use finalize_types::*;
27
28mod register_types;
29pub use register_types::*;
30
31mod registers;
32pub use registers::*;
33
34mod authorize;
35mod deploy;
36mod evaluate;
37mod execute;
38mod helpers;
39
40use crate::{CallMetrics, Process, Trace, TranslationAssignment, compute_console_dynamic_or_external_record_id};
41use console::{
42    account::{Address, PrivateKey},
43    network::prelude::*,
44    program::{
45        Argument,
46        DynamicFuture,
47        DynamicRecord,
48        Entry,
49        EntryType,
50        FinalizeType,
51        Future,
52        Identifier,
53        Literal,
54        Locator,
55        Owner as RecordOwner,
56        Plaintext,
57        PlaintextType,
58        ProgramID,
59        Record,
60        RecordType,
61        RegisterType,
62        Request,
63        Response,
64        ToFields,
65        U8,
66        U16,
67        Value,
68        ValueType,
69    },
70    types::{Field, Group},
71};
72use snarkvm_ledger_block::{Deployment, Transaction, Transition};
73use snarkvm_synthesizer_error::*;
74use snarkvm_synthesizer_program::{
75    CallOperator,
76    Closure,
77    FinalizeGlobalState,
78    FinalizeStoreTrait,
79    Function,
80    Instruction,
81    Operand,
82    Program,
83    RegistersCircuit,
84    RegistersSigner,
85    RegistersTrait,
86    StackTrait,
87};
88use snarkvm_synthesizer_snark::{Certificate, ProvingKey, UniversalSRS, VerifyingKey};
89
90use aleo_std::prelude::{finish, lap, timer};
91use indexmap::IndexMap;
92#[cfg(feature = "locktick")]
93use locktick::parking_lot::RwLock;
94#[cfg(not(feature = "locktick"))]
95use parking_lot::RwLock;
96use rand::{CryptoRng, RngExt as Rng, SeedableRng, rngs::StdRng};
97use std::{
98    cell::OnceCell,
99    sync::{Arc, Weak},
100};
101
102#[cfg(not(feature = "serial"))]
103use rayon::prelude::*;
104
105pub type Assignments<N> = Arc<RwLock<Vec<(circuit::Assignment<<N as Environment>::Field>, CallMetrics<N>)>>>;
106/// A stack of translations for the transitions in the execution. Each function execution level pushes a new group,
107/// and translations for dynamic calls made at that level are collected into the top group. When the transition is
108/// inserted, the top group is popped and its translations are associated with that transition (the caller's
109/// transition ID). Each translation datum is paired with the proving key for its record type.
110pub type Translations<N> = Arc<RwLock<Vec<Vec<(TranslationAssignment<N>, ProvingKey<N>)>>>>;
111
112/// The `CallStack` is used to track the current state of the program execution.
113#[derive(Clone, Debug)]
114pub enum CallStack<N: Network> {
115    /// Authorize an `Execute` transaction.
116    Authorize(Vec<Request<N>>, Option<PrivateKey<N>>, Authorization<N>),
117    /// Mock an evaluation for cost estimation.
118    AuthorizeMocked(Vec<Request<N>>, Address<N>, Authorization<N>),
119    /// Synthesize a function circuit before a `Deploy` transaction.
120    Synthesize(Vec<Request<N>>, PrivateKey<N>, Authorization<N>),
121    /// Validate a `Deploy` transaction's function circuit.
122    CheckDeployment(Vec<Request<N>>, PrivateKey<N>, Assignments<N>, Option<u64>, Option<u64>),
123    /// Evaluate a function.
124    Evaluate(Authorization<N>),
125    /// Execute a function and produce a proof.
126    Execute(Authorization<N>, Arc<RwLock<Trace<N>>>, Translations<N>),
127    /// Execute a function and create the circuit assignment.
128    PackageRun(Vec<Request<N>>, PrivateKey<N>, Assignments<N>),
129}
130
131// impl to_string for CallStack<N>
132impl<N: Network> CallStack<N> {
133    fn type_as_string(&self) -> String {
134        match self {
135            CallStack::Authorize(..) => "Authorize".to_string(),
136            CallStack::AuthorizeMocked(..) => "Mock".to_string(),
137            CallStack::Synthesize(..) => "Synthesize".to_string(),
138            CallStack::CheckDeployment(..) => "CheckDeployment".to_string(),
139            CallStack::Evaluate(..) => "Evaluate".to_string(),
140            CallStack::Execute(..) => "Execute".to_string(),
141            CallStack::PackageRun(..) => "PackageRun".to_string(),
142        }
143    }
144}
145
146impl<N: Network> CallStack<N> {
147    /// Initializes a call stack as `Self::Evaluate`.
148    pub fn evaluate(authorization: Authorization<N>) -> Result<Self> {
149        Ok(CallStack::Evaluate(authorization))
150    }
151
152    /// Initializes a call stack as `Self::Execute`.
153    pub fn execute(
154        authorization: Authorization<N>,
155        trace: Arc<RwLock<Trace<N>>>,
156        translations: Translations<N>,
157    ) -> Result<Self> {
158        Ok(CallStack::Execute(authorization, trace, translations))
159    }
160}
161
162impl<N: Network> CallStack<N> {
163    /// Returns a new and independent replica of the call stack.
164    pub fn replicate(&self) -> Self {
165        match self {
166            CallStack::Authorize(requests, private_key, authorization) => {
167                CallStack::Authorize(requests.clone(), *private_key, authorization.replicate())
168            }
169            CallStack::AuthorizeMocked(requests, address, authorization) => {
170                CallStack::AuthorizeMocked(requests.clone(), *address, authorization.replicate())
171            }
172            CallStack::Synthesize(requests, private_key, authorization) => {
173                CallStack::Synthesize(requests.clone(), *private_key, authorization.replicate())
174            }
175            CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit, variable_limit) => {
176                CallStack::CheckDeployment(
177                    requests.clone(),
178                    *private_key,
179                    Arc::new(RwLock::new(assignments.read().clone())),
180                    *constraint_limit,
181                    *variable_limit,
182                )
183            }
184            CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()),
185            CallStack::Execute(authorization, trace, translations) => CallStack::Execute(
186                authorization.replicate(),
187                Arc::new(RwLock::new(trace.read().clone())),
188                Arc::new(RwLock::new(translations.read().clone())),
189            ),
190            CallStack::PackageRun(requests, private_key, assignments) => {
191                CallStack::PackageRun(requests.clone(), *private_key, Arc::new(RwLock::new(assignments.read().clone())))
192            }
193        }
194    }
195
196    /// Pushes the request to the stack.
197    pub fn push(&mut self, request: Request<N>) -> Result<()> {
198        match self {
199            CallStack::Authorize(requests, ..)
200            | CallStack::AuthorizeMocked(requests, ..)
201            | CallStack::Synthesize(requests, ..)
202            | CallStack::CheckDeployment(requests, ..)
203            | CallStack::PackageRun(requests, ..) => {
204                // Check that the number of requests does not exceed the maximum.
205                ensure!(
206                    requests.len() < Transaction::<N>::MAX_TRANSITIONS,
207                    "The number of requests in the authorization must be less than '{}'.",
208                    Transaction::<N>::MAX_TRANSITIONS
209                );
210                // Push the request to the stack.
211                requests.push(request)
212            }
213            CallStack::Evaluate(authorization) => authorization.push(request)?,
214            CallStack::Execute(authorization, ..) => authorization.push(request)?,
215        }
216        Ok(())
217    }
218
219    /// Pops the request from the stack.
220    pub fn pop(&mut self) -> Result<Request<N>> {
221        match self {
222            CallStack::Authorize(requests, ..)
223            | CallStack::AuthorizeMocked(requests, ..)
224            | CallStack::Synthesize(requests, ..)
225            | CallStack::CheckDeployment(requests, ..)
226            | CallStack::PackageRun(requests, ..) => {
227                requests.pop().ok_or_else(|| anyhow!("No more requests on the stack"))
228            }
229            CallStack::Evaluate(authorization) => authorization.next(),
230            CallStack::Execute(authorization, ..) => authorization.next(),
231        }
232    }
233
234    /// Peeks at the next request from the stack.
235    pub fn peek(&self) -> Result<Request<N>> {
236        match self {
237            CallStack::Authorize(requests, ..)
238            | CallStack::AuthorizeMocked(requests, ..)
239            | CallStack::Synthesize(requests, ..)
240            | CallStack::CheckDeployment(requests, ..)
241            | CallStack::PackageRun(requests, ..) => {
242                requests.last().cloned().ok_or_else(|| anyhow!("No more requests on the stack"))
243            }
244            CallStack::Evaluate(authorization) => authorization.peek_next(),
245            CallStack::Execute(authorization, ..) => authorization.peek_next(),
246        }
247    }
248}
249
250#[derive(Clone)]
251pub struct Stack<N: Network> {
252    /// The program (record types, structs, functions).
253    program: Program<N>,
254    /// A reference to the global stack map.
255    stacks: Weak<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
256    /// The register types for the program constructor, if it exists.
257    constructor_types: Arc<RwLock<Option<FinalizeTypes<N>>>>,
258    /// The mapping of closure and function names to their register types.
259    register_types: Arc<RwLock<IndexMap<Identifier<N>, RegisterTypes<N>>>>,
260    /// The mapping of finalize names to their register types.
261    finalize_types: Arc<RwLock<IndexMap<Identifier<N>, FinalizeTypes<N>>>>,
262    /// The mapping of view function names to their register types.
263    view_types: Arc<RwLock<IndexMap<Identifier<N>, FinalizeTypes<N>>>>,
264    /// The universal SRS.
265    universal_srs: UniversalSRS<N>,
266    /// The mapping of function name or record name to proving key.
267    /// Function names map to function proving keys, record names map to translation proving keys.
268    proving_keys: Arc<RwLock<IndexMap<Identifier<N>, ProvingKey<N>>>>,
269    /// The mapping of function name or record name to verifying key.
270    /// Function names map to function verifying keys, record names map to translation verifying keys.
271    verifying_keys: Arc<RwLock<IndexMap<Identifier<N>, VerifyingKey<N>>>>,
272    /// The program address.
273    program_address: Address<N>,
274    /// The program checksum.
275    program_checksum: [U8<N>; 32],
276    /// The program edition.
277    program_edition: U16<N>,
278    /// The number of amendments applied to the current program edition.
279    program_amendment_count: u64,
280    /// The program owner.
281    program_owner: Option<Address<N>>,
282}
283
284impl<N: Network> Stack<N> {
285    /// Initializes a new stack given the process and the program.
286    pub fn new(process: &Process<N>, program: &Program<N>) -> Result<Self> {
287        // Retrieve the program ID.
288        let program_id = program.id();
289        // Check that the program is well-formed.
290        check_program_is_well_formed(program)?;
291
292        // If the program exists in the process, check that the new program is valid.
293        if let Ok(existing_stack) = process.get_stack(program_id) {
294            // Ensure the program is not `credits.aleo`.
295            ensure!(program_id != &ProgramID::from_str("credits.aleo")?, "Cannot re-initialize the 'credits.aleo'.");
296            // Get the existing program.
297            let existing_program = existing_stack.program();
298            // If the existing program does not have a constructor, check that the new program matches the existing program.
299            // Otherwise, ensure that the upgrade is valid.
300            match existing_program.contains_constructor() {
301                false => ensure!(
302                    existing_stack.program() == program,
303                    "Program '{program_id}' already exists with different contents."
304                ),
305                true => Self::check_upgrade_is_valid(existing_program, program)?,
306            }
307        }
308
309        // Return the stack.
310        Stack::initialize(process, program)
311    }
312
313    /// Partially initializes a new stack, given the process and the program, without checking for validity.
314    /// Unlike `Stack::new`, this method accepts an explicit edition and skips upgrade validation,
315    /// which is necessary for amendments that preserve the existing edition rather than incrementing it.
316    /// A `Stack` created by this method must also call `initialize_and_check`.
317    pub fn new_raw(process: &Process<N>, program: &Program<N>, edition: u16) -> Result<Self> {
318        // Check that the program is well-formed.
319        check_program_is_well_formed(program)?;
320        // Return the stack.
321        Stack::create_raw(process, program, edition)
322    }
323
324    /// Initializes and checks the register state and well-formedness of the stack, even if it has already been initialized.
325    pub fn initialize_and_check(&self, process: &Process<N>) -> Result<()> {
326        // Acquire the locks for the constructor, register, finalize, and view types.
327        let mut constructor_types = self.constructor_types.write();
328        let mut register_types = self.register_types.write();
329        let mut finalize_types = self.finalize_types.write();
330        let mut view_types = self.view_types.write();
331
332        // Clear the existing constructor, closure, function, and view types.
333        constructor_types.take();
334        register_types.clear();
335        finalize_types.clear();
336        view_types.clear();
337
338        // Add all the imports into the stack.
339        for import in self.program.imports().keys() {
340            // Ensure that the program does not import itself.
341            ensure!(import != self.program.id(), "Program cannot import itself");
342            // Ensure the program imports all exist in the process already.
343            if !process.contains_program(import) {
344                bail!("Cannot add program '{}' because its import '{import}' must be added first", self.program.id())
345            }
346        }
347
348        // Add the constructor to the stack if it exists.
349        if let Some(constructor) = self.program.constructor() {
350            // Compute the constructor types.
351            let types = FinalizeTypes::from_constructor(self, constructor)?;
352            // Add the constructor types to the stack.
353            constructor_types.replace(types);
354        }
355
356        // Add the program closures to the stack.
357        for closure in self.program.closures().values() {
358            // Retrieve the closure name.
359            let name = closure.name();
360            // Ensure the closure name is not already added.
361            ensure!(!register_types.contains_key(name), "Closure '{name}' already exists");
362            // Compute the register types.
363            let types = RegisterTypes::from_closure(self, closure)?;
364            // Add the closure name and register types to the stack.
365            register_types.insert(*name, types);
366        }
367
368        // Add the program functions to the stack.
369        for function in self.program.functions().values() {
370            // Retrieve the function name.
371            let name = function.name();
372            // Ensure the function name is not already added.
373            ensure!(!register_types.contains_key(name), "Function '{name}' already exists");
374            // Compute the register types.
375            let types = RegisterTypes::from_function(self, function)?;
376            // Add the function name and register types to the stack.
377            register_types.insert(*name, types);
378
379            // If the function contains a finalize, insert it.
380            if let Some(finalize) = function.finalize_logic() {
381                // Compute the finalize types.
382                let types = FinalizeTypes::from_finalize(self, finalize)?;
383                // Add the finalize name and finalize types to the stack.
384                finalize_types.insert(*name, types);
385            }
386        }
387
388        // Type-check every view function and cache the result. The cached types are read by
389        // both the external view path (`evaluate_view_at_height`) and the in-block call path
390        // when finalize calls a view, so we avoid recomputing them on every invocation.
391        for view in self.program.views().values() {
392            let name = view.name();
393            ensure!(!view_types.contains_key(name), "View '{name}' already exists");
394            let types = FinalizeTypes::from_view(self, view)?;
395            view_types.insert(*name, types);
396        }
397
398        // Drop the locks since the types have been initialized.
399        drop(constructor_types);
400        drop(register_types);
401        drop(finalize_types);
402        drop(view_types);
403
404        // Check that the functions are valid.
405        for function in self.program.functions().values() {
406            // Determine the minimum number of calls for the function.
407            // This includes a safety check against maximum allowed number of calls.
408            self.get_minimum_number_of_calls(function.name())?;
409        }
410        Ok(())
411    }
412
413    /// Returns the constructor types for the program.
414    #[inline]
415    pub fn get_constructor_types(&self) -> Result<FinalizeTypes<N>> {
416        // Retrieve the constructor types.
417        match self.constructor_types.read().as_ref() {
418            Some(constructor_types) => Ok(constructor_types.clone()),
419            None => bail!("Constructor types do not exist"),
420        }
421    }
422
423    /// Returns the register types for the given closure or function name.
424    #[inline]
425    pub fn get_register_types(&self, name: &Identifier<N>) -> Result<RegisterTypes<N>> {
426        // Retrieve the register types.
427        match self.register_types.read().get(name) {
428            Some(register_types) => Ok(register_types.clone()),
429            None => bail!("Register types for '{name}' do not exist"),
430        }
431    }
432
433    /// Returns the register types for the given finalize name.
434    #[inline]
435    pub fn get_finalize_types(&self, name: &Identifier<N>) -> Result<FinalizeTypes<N>> {
436        // Retrieve the finalize types.
437        match self.finalize_types.read().get(name) {
438            Some(finalize_types) => Ok(finalize_types.clone()),
439            None => bail!("Finalize types for '{name}' do not exist"),
440        }
441    }
442
443    /// Returns the register types for the given view function name.
444    #[inline]
445    pub fn get_view_types(&self, name: &Identifier<N>) -> Result<FinalizeTypes<N>> {
446        match self.view_types.read().get(name) {
447            Some(view_types) => Ok(view_types.clone()),
448            None => bail!("View types for '{name}' do not exist"),
449        }
450    }
451
452    /// Inserts the proving key if the program ID is 'credits.aleo'.
453    fn try_insert_credits_function_proving_key(&self, function_name: &Identifier<N>) -> Result<()> {
454        // If the program is 'credits.aleo' and it does not exist yet, load the proving key directly.
455        if self.program_id() == &ProgramID::from_str("credits.aleo")?
456            && !self.proving_keys.read().contains_key(function_name)
457        {
458            // Load the 'credits.aleo' function proving key.
459            let proving_key = N::get_credits_proving_key(function_name.to_string())?;
460            // Insert the 'credits.aleo' function proving key.
461            self.insert_proving_key(function_name, ProvingKey::new(proving_key.clone()))?;
462        }
463        Ok(())
464    }
465}
466
467impl<N: Network> PartialEq for Stack<N> {
468    fn eq(&self, other: &Self) -> bool {
469        self.program == other.program
470            && *self.register_types.read() == *other.register_types.read()
471            && *self.finalize_types.read() == *other.finalize_types.read()
472    }
473}
474
475impl<N: Network> Eq for Stack<N> {}
476
477// A helper enum to avoid cloning stacks.
478#[derive(Clone)]
479pub(crate) enum StackRef<'a, N: Network> {
480    // Self's stack.
481    Internal(&'a Stack<N>),
482    // An external stack.
483    External(Arc<Stack<N>>),
484}
485
486impl<N: Network> Deref for StackRef<'_, N> {
487    type Target = Stack<N>;
488
489    fn deref(&self) -> &Self::Target {
490        match self {
491            StackRef::Internal(stack) => stack,
492            StackRef::External(stack) => stack,
493        }
494    }
495}
496
497// A helper function to check that a program is well-formed.
498fn check_program_is_well_formed<N: Network>(program: &Program<N>) -> Result<()> {
499    // Ensure the program contains functions.
500    ensure!(!program.functions().is_empty(), "No functions present in the deployment for program '{}'", program.id());
501
502    // Serialize the program into bytes.
503    let program_bytes = program.to_bytes_le()?;
504    // Ensure the program deserializes from bytes correctly.
505    ensure!(program == &Program::from_bytes_le(&program_bytes)?, "Program byte serialization failed");
506
507    // Serialize the program into string.
508    let program_string = program.to_string();
509    // Ensure the program deserializes from a string correctly.
510    ensure!(program == &Program::from_str(&program_string)?, "Program string serialization failed");
511
512    Ok(())
513}