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