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, cost_in_microcredits_v2, traits::*};
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        Value,
63        ValueType,
64    },
65    types::{Field, Group},
66};
67use ledger_block::{Deployment, Transaction, Transition};
68use synthesizer_program::{CallOperator, Closure, Function, Instruction, Operand, Program, traits::*};
69use synthesizer_snark::{Certificate, ProvingKey, UniversalSRS, VerifyingKey};
70
71use aleo_std::prelude::{finish, lap, timer};
72use indexmap::IndexMap;
73#[cfg(feature = "locktick")]
74use locktick::parking_lot::RwLock;
75#[cfg(not(feature = "locktick"))]
76use parking_lot::RwLock;
77use std::sync::{Arc, Weak};
78
79#[cfg(not(feature = "serial"))]
80use rayon::prelude::*;
81
82pub type Assignments<N> = Arc<RwLock<Vec<(circuit::Assignment<<N as Environment>::Field>, CallMetrics<N>)>>>;
83
84#[derive(Clone)]
85pub enum CallStack<N: Network> {
86    Authorize(Vec<Request<N>>, PrivateKey<N>, Authorization<N>),
87    Synthesize(Vec<Request<N>>, PrivateKey<N>, Authorization<N>),
88    CheckDeployment(Vec<Request<N>>, PrivateKey<N>, Assignments<N>, Option<u64>, Option<u64>),
89    Evaluate(Authorization<N>),
90    Execute(Authorization<N>, Arc<RwLock<Trace<N>>>),
91    PackageRun(Vec<Request<N>>, PrivateKey<N>, Assignments<N>),
92}
93
94impl<N: Network> CallStack<N> {
95    /// Initializes a call stack as `Self::Evaluate`.
96    pub fn evaluate(authorization: Authorization<N>) -> Result<Self> {
97        Ok(CallStack::Evaluate(authorization))
98    }
99
100    /// Initializes a call stack as `Self::Execute`.
101    pub fn execute(authorization: Authorization<N>, trace: Arc<RwLock<Trace<N>>>) -> Result<Self> {
102        Ok(CallStack::Execute(authorization, trace))
103    }
104}
105
106impl<N: Network> CallStack<N> {
107    /// Returns a new and independent replica of the call stack.
108    pub fn replicate(&self) -> Self {
109        match self {
110            CallStack::Authorize(requests, private_key, authorization) => {
111                CallStack::Authorize(requests.clone(), *private_key, authorization.replicate())
112            }
113            CallStack::Synthesize(requests, private_key, authorization) => {
114                CallStack::Synthesize(requests.clone(), *private_key, authorization.replicate())
115            }
116            CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit, variable_limit) => {
117                CallStack::CheckDeployment(
118                    requests.clone(),
119                    *private_key,
120                    Arc::new(RwLock::new(assignments.read().clone())),
121                    *constraint_limit,
122                    *variable_limit,
123                )
124            }
125            CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()),
126            CallStack::Execute(authorization, trace) => {
127                CallStack::Execute(authorization.replicate(), Arc::new(RwLock::new(trace.read().clone())))
128            }
129            CallStack::PackageRun(requests, private_key, assignments) => {
130                CallStack::PackageRun(requests.clone(), *private_key, Arc::new(RwLock::new(assignments.read().clone())))
131            }
132        }
133    }
134
135    /// Pushes the request to the stack.
136    pub fn push(&mut self, request: Request<N>) -> Result<()> {
137        match self {
138            CallStack::Authorize(requests, ..)
139            | CallStack::Synthesize(requests, ..)
140            | CallStack::CheckDeployment(requests, ..)
141            | CallStack::PackageRun(requests, ..) => {
142                // Check that the number of requests does not exceed the maximum.
143                ensure!(
144                    requests.len() < Transaction::<N>::MAX_TRANSITIONS,
145                    "The number of requests in the authorization must be less than '{}'.",
146                    Transaction::<N>::MAX_TRANSITIONS
147                );
148                // Push the request to the stack.
149                requests.push(request)
150            }
151            CallStack::Evaluate(authorization) => authorization.push(request)?,
152            CallStack::Execute(authorization, ..) => authorization.push(request)?,
153        }
154        Ok(())
155    }
156
157    /// Pops the request from the stack.
158    pub fn pop(&mut self) -> Result<Request<N>> {
159        match self {
160            CallStack::Authorize(requests, ..)
161            | CallStack::Synthesize(requests, ..)
162            | CallStack::CheckDeployment(requests, ..)
163            | CallStack::PackageRun(requests, ..) => {
164                requests.pop().ok_or_else(|| anyhow!("No more requests on the stack"))
165            }
166            CallStack::Evaluate(authorization) => authorization.next(),
167            CallStack::Execute(authorization, ..) => authorization.next(),
168        }
169    }
170
171    /// Peeks at the next request from the stack.
172    pub fn peek(&mut self) -> Result<Request<N>> {
173        match self {
174            CallStack::Authorize(requests, ..)
175            | CallStack::Synthesize(requests, ..)
176            | CallStack::CheckDeployment(requests, ..)
177            | CallStack::PackageRun(requests, ..) => {
178                requests.last().cloned().ok_or_else(|| anyhow!("No more requests on the stack"))
179            }
180            CallStack::Evaluate(authorization) => authorization.peek_next(),
181            CallStack::Execute(authorization, ..) => authorization.peek_next(),
182        }
183    }
184}
185
186#[derive(Clone)]
187pub struct Stack<N: Network> {
188    /// The program (record types, structs, functions).
189    program: Program<N>,
190    /// A reference to the global stack map.
191    stacks: Weak<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
192    /// The mapping of closure and function names to their register types.
193    register_types: IndexMap<Identifier<N>, RegisterTypes<N>>,
194    /// The mapping of finalize names to their register types.
195    finalize_types: IndexMap<Identifier<N>, FinalizeTypes<N>>,
196    /// The universal SRS.
197    universal_srs: UniversalSRS<N>,
198    /// The mapping of function name to proving key.
199    proving_keys: Arc<RwLock<IndexMap<Identifier<N>, ProvingKey<N>>>>,
200    /// The mapping of function name to verifying key.
201    verifying_keys: Arc<RwLock<IndexMap<Identifier<N>, VerifyingKey<N>>>>,
202    /// The program address.
203    program_address: Address<N>,
204}
205
206impl<N: Network> Stack<N> {
207    /// Initializes a new stack, if it does not already exist, given the process and the program.
208    #[inline]
209    pub fn new(process: &Process<N>, program: &Program<N>) -> Result<Self> {
210        // Retrieve the program ID.
211        let program_id = program.id();
212        // Ensure the program does not already exist in the process.
213        ensure!(!process.contains_program(program_id), "Program '{program_id}' already exists");
214        // Ensure the program contains functions.
215        ensure!(!program.functions().is_empty(), "No functions present in the deployment for program '{program_id}'");
216
217        // Serialize the program into bytes.
218        let program_bytes = program.to_bytes_le()?;
219        // Ensure the program deserializes from bytes correctly.
220        ensure!(program == &Program::from_bytes_le(&program_bytes)?, "Program byte serialization failed");
221
222        // Serialize the program into string.
223        let program_string = program.to_string();
224        // Ensure the program deserializes from a string correctly.
225        ensure!(program == &Program::from_str(&program_string)?, "Program string serialization failed");
226
227        // Return the stack.
228        Stack::initialize(process, program)
229    }
230}
231
232impl<N: Network> StackKeys<N> for Stack<N> {
233    /// Returns `true` if the proving key for the given function name exists.
234    #[inline]
235    fn contains_proving_key(&self, function_name: &Identifier<N>) -> bool {
236        self.proving_keys.read().contains_key(function_name)
237    }
238
239    /// Returns the proving key for the given function name.
240    #[inline]
241    fn get_proving_key(&self, function_name: &Identifier<N>) -> Result<ProvingKey<N>> {
242        // If the program is 'credits.aleo', try to load the proving key, if it does not exist.
243        self.try_insert_credits_function_proving_key(function_name)?;
244        // Return the proving key, if it exists.
245        match self.proving_keys.read().get(function_name) {
246            Some(pk) => Ok(pk.clone()),
247            None => bail!("Proving key not found for: {}/{}", self.program.id(), function_name),
248        }
249    }
250
251    /// Inserts the given proving key for the given function name.
252    #[inline]
253    fn insert_proving_key(&self, function_name: &Identifier<N>, proving_key: ProvingKey<N>) -> Result<()> {
254        // Ensure the function name exists in the program.
255        ensure!(
256            self.program.contains_function(function_name),
257            "Function '{function_name}' does not exist in program '{}'.",
258            self.program.id()
259        );
260        // Insert the proving key.
261        self.proving_keys.write().insert(*function_name, proving_key);
262        Ok(())
263    }
264
265    /// Removes the proving key for the given function name.
266    #[inline]
267    fn remove_proving_key(&self, function_name: &Identifier<N>) {
268        self.proving_keys.write().shift_remove(function_name);
269    }
270
271    /// Returns `true` if the verifying key for the given function name exists.
272    #[inline]
273    fn contains_verifying_key(&self, function_name: &Identifier<N>) -> bool {
274        self.verifying_keys.read().contains_key(function_name)
275    }
276
277    /// Returns the verifying key for the given function name.
278    #[inline]
279    fn get_verifying_key(&self, function_name: &Identifier<N>) -> Result<VerifyingKey<N>> {
280        // Return the verifying key, if it exists.
281        match self.verifying_keys.read().get(function_name) {
282            Some(vk) => Ok(vk.clone()),
283            None => bail!("Verifying key not found for: {}/{}", self.program.id(), function_name),
284        }
285    }
286
287    /// Inserts the given verifying key for the given function name.
288    #[inline]
289    fn insert_verifying_key(&self, function_name: &Identifier<N>, verifying_key: VerifyingKey<N>) -> Result<()> {
290        // Ensure the function name exists in the program.
291        ensure!(
292            self.program.contains_function(function_name),
293            "Function '{function_name}' does not exist in program '{}'.",
294            self.program.id()
295        );
296        // Insert the verifying key.
297        self.verifying_keys.write().insert(*function_name, verifying_key);
298        Ok(())
299    }
300
301    /// Removes the verifying key for the given function name.
302    #[inline]
303    fn remove_verifying_key(&self, function_name: &Identifier<N>) {
304        self.verifying_keys.write().shift_remove(function_name);
305    }
306}
307
308impl<N: Network> StackProgram<N> for Stack<N> {
309    /// Returns the program.
310    #[inline]
311    fn program(&self) -> &Program<N> {
312        &self.program
313    }
314
315    /// Returns the program ID.
316    #[inline]
317    fn program_id(&self) -> &ProgramID<N> {
318        self.program.id()
319    }
320
321    /// Returns the program address.
322    #[inline]
323    fn program_address(&self) -> &Address<N> {
324        &self.program_address
325    }
326
327    /// Returns the external stack for the given program ID.
328    ///
329    /// Attention - this function is used to check the existence of the external program.
330    /// Developers should explicitly handle the error case so as to not default to the main program.
331    #[inline]
332    fn get_external_stack(&self, program_id: &ProgramID<N>) -> Result<Arc<Stack<N>>> {
333        // Check that the program ID is not itself.
334        ensure!(
335            program_id != self.program.id(),
336            "Attempted to get the main program '{program_id}' as an external program."
337        );
338        // Check that the program ID is imported by the program.
339        ensure!(self.program.contains_import(program_id), "External program '{program_id}' is not imported.");
340        // Upgrade the weak reference to the process-level stack map and retrieve the external stack.
341        self.stacks
342            .upgrade()
343            .ok_or_else(|| anyhow!("Process-level stack map does not exist"))?
344            .read()
345            .get(program_id)
346            .cloned()
347            .ok_or_else(|| anyhow!("External stack for '{program_id}' does not exist"))
348    }
349
350    /// Returns the function with the given function name.
351    #[inline]
352    fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>> {
353        self.program.get_function(function_name)
354    }
355
356    /// Returns a reference to the function with the given function name.
357    #[inline]
358    fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>> {
359        self.program.get_function_ref(function_name)
360    }
361
362    /// Returns the expected number of calls for the given function name.
363    #[inline]
364    fn get_number_of_calls(&self, function_name: &Identifier<N>) -> Result<usize> {
365        // Initialize the base number of calls.
366        let mut num_calls = 1;
367        // Initialize a queue of functions to check.
368        let mut queue = vec![(StackRef::Internal(self), *function_name)];
369        // Iterate over the queue.
370        while let Some((stack_ref, function_name)) = queue.pop() {
371            // Ensure that the number of calls does not exceed the maximum.
372            // Note that one transition is reserved for the fee.
373            ensure!(
374                num_calls < Transaction::<N>::MAX_TRANSITIONS,
375                "Number of calls must be less than '{}'",
376                Transaction::<N>::MAX_TRANSITIONS
377            );
378            // Determine the number of calls for the function.
379            for instruction in stack_ref.get_function_ref(&function_name)?.instructions() {
380                if let Instruction::Call(call) = instruction {
381                    // Determine if this is a function call.
382                    if call.is_function_call(&*stack_ref)? {
383                        // Increment by the number of calls.
384                        num_calls += 1;
385                        // Add the function to the queue.
386                        match call.operator() {
387                            CallOperator::Locator(locator) => {
388                                queue.push((
389                                    StackRef::External(stack_ref.get_external_stack(locator.program_id())?),
390                                    *locator.resource(),
391                                ));
392                            }
393                            CallOperator::Resource(resource) => {
394                                queue.push((stack_ref.clone(), *resource));
395                            }
396                        }
397                    }
398                }
399            }
400        }
401        // Return the number of calls.
402        Ok(num_calls)
403    }
404
405    /// Returns a value for the given value type.
406    fn sample_value<R: Rng + CryptoRng>(
407        &self,
408        burner_address: &Address<N>,
409        value_type: &ValueType<N>,
410        rng: &mut R,
411    ) -> Result<Value<N>> {
412        match value_type {
413            ValueType::Constant(plaintext_type)
414            | ValueType::Public(plaintext_type)
415            | ValueType::Private(plaintext_type) => Ok(Value::Plaintext(self.sample_plaintext(plaintext_type, rng)?)),
416            ValueType::Record(record_name) => {
417                Ok(Value::Record(self.sample_record(burner_address, record_name, Group::rand(rng), rng)?))
418            }
419            ValueType::ExternalRecord(locator) => {
420                // Retrieve the external stack.
421                let stack = self.get_external_stack(locator.program_id())?;
422                // Sample the output.
423                Ok(Value::Record(stack.sample_record(burner_address, locator.resource(), Group::rand(rng), rng)?))
424            }
425            ValueType::Future(locator) => Ok(Value::Future(self.sample_future(locator, rng)?)),
426        }
427    }
428
429    /// Returns a record for the given record name, with the given burner address and nonce.
430    fn sample_record<R: Rng + CryptoRng>(
431        &self,
432        burner_address: &Address<N>,
433        record_name: &Identifier<N>,
434        nonce: Group<N>,
435        rng: &mut R,
436    ) -> Result<Record<N, Plaintext<N>>> {
437        // Sample a record.
438        let record = self.sample_record_internal(burner_address, record_name, nonce, 0, rng)?;
439        // Ensure the record matches the value type.
440        self.matches_record(&record, record_name)?;
441        // Return the record.
442        Ok(record)
443    }
444
445    /// Returns a record for the given record name, deriving the nonce from tvk and index.
446    fn sample_record_using_tvk<R: Rng + CryptoRng>(
447        &self,
448        burner_address: &Address<N>,
449        record_name: &Identifier<N>,
450        tvk: Field<N>,
451        index: Field<N>,
452        rng: &mut R,
453    ) -> Result<Record<N, Plaintext<N>>> {
454        // Compute the randomizer.
455        let randomizer = N::hash_to_scalar_psd2(&[tvk, index])?;
456        // Construct the record nonce from that randomizer.
457        let record_nonce = N::g_scalar_multiply(&randomizer);
458        // Sample the record with that nonce.
459        self.sample_record(burner_address, record_name, record_nonce, rng)
460    }
461}
462
463impl<N: Network> StackProgramTypes<N> for Stack<N> {
464    /// Returns the register types for the given closure or function name.
465    #[inline]
466    fn get_register_types(&self, name: &Identifier<N>) -> Result<&RegisterTypes<N>> {
467        // Retrieve the register types.
468        self.register_types.get(name).ok_or_else(|| anyhow!("Register types for '{name}' do not exist"))
469    }
470
471    /// Returns the register types for the given finalize name.
472    #[inline]
473    fn get_finalize_types(&self, name: &Identifier<N>) -> Result<&FinalizeTypes<N>> {
474        // Retrieve the finalize types.
475        self.finalize_types.get(name).ok_or_else(|| anyhow!("Finalize types for '{name}' do not exist"))
476    }
477}
478
479impl<N: Network> Stack<N> {
480    /// Inserts the proving key if the program ID is 'credits.aleo'.
481    fn try_insert_credits_function_proving_key(&self, function_name: &Identifier<N>) -> Result<()> {
482        // If the program is 'credits.aleo' and it does not exist yet, load the proving key directly.
483        if self.program_id() == &ProgramID::from_str("credits.aleo")?
484            && !self.proving_keys.read().contains_key(function_name)
485        {
486            // Load the 'credits.aleo' function proving key.
487            let proving_key = N::get_credits_proving_key(function_name.to_string())?;
488            // Insert the 'credits.aleo' function proving key.
489            self.insert_proving_key(function_name, ProvingKey::new(proving_key.clone()))?;
490        }
491        Ok(())
492    }
493}
494
495impl<N: Network> PartialEq for Stack<N> {
496    fn eq(&self, other: &Self) -> bool {
497        self.program == other.program
498            && self.register_types == other.register_types
499            && self.finalize_types == other.finalize_types
500    }
501}
502
503impl<N: Network> Eq for Stack<N> {}
504
505// A helper enum to avoid cloning stacks.
506#[derive(Clone)]
507pub(crate) enum StackRef<'a, N: Network> {
508    // Self's stack.
509    Internal(&'a Stack<N>),
510    // An external stack.
511    External(Arc<Stack<N>>),
512}
513
514impl<N: Network> Deref for StackRef<'_, N> {
515    type Target = Stack<N>;
516
517    fn deref(&self) -> &Self::Target {
518        match self {
519            StackRef::Internal(stack) => stack,
520            StackRef::External(stack) => stack,
521        }
522    }
523}