Skip to main content

snarkvm_synthesizer_program/traits/
stack_and_registers.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
16use std::sync::Arc;
17
18use crate::{FinalizeGlobalState, FinalizeStoreTrait, Function, Operand, Program};
19use console::{
20    account::Group,
21    network::Network,
22    prelude::{Result, bail},
23    program::{
24        Future,
25        Identifier,
26        Literal,
27        Locator,
28        Plaintext,
29        PlaintextType,
30        ProgramID,
31        Record,
32        Register,
33        RegisterType,
34        Request,
35        StructType,
36        Value,
37        ValueType,
38    },
39    types::{Address, Field, U8, U16},
40};
41use rand::{CryptoRng, Rng};
42use snarkvm_synthesizer_snark::{ProvingKey, VerifyingKey};
43
44/// This trait is intended to be implemented only by `snarkvm_synthesizer_process::Stack`.
45///
46/// We make it a trait only to avoid circular dependencies.
47pub trait StackTrait<N: Network> {
48    /// Returns `true` if the proving key for the given name exists.
49    /// The name can be a function name or a record name (for translation keys).
50    fn contains_proving_key(&self, function_or_record_name: &Identifier<N>) -> bool;
51
52    /// Returns the proving key for the given name.
53    /// The name can be a function name or a record name (for translation keys).
54    fn get_proving_key(&self, function_or_record_name: &Identifier<N>) -> Result<ProvingKey<N>>;
55
56    /// Inserts the proving key for the given name.
57    /// The name can be a function name or a record name (for translation keys).
58    fn insert_proving_key(&self, function_or_record_name: &Identifier<N>, proving_key: ProvingKey<N>) -> Result<()>;
59
60    /// Removes the proving key for the given name.
61    /// The name can be a function name or a record name (for translation keys).
62    fn remove_proving_key(&self, function_or_record_name: &Identifier<N>);
63
64    /// Returns `true` if the verifying key for the given name exists.
65    /// The name can be a function name or a record name (for translation keys).
66    fn contains_verifying_key(&self, function_or_record_name: &Identifier<N>) -> bool;
67
68    /// Returns the verifying key for the given name.
69    /// The name can be a function name or a record name (for translation keys).
70    fn get_verifying_key(&self, function_or_record_name: &Identifier<N>) -> Result<VerifyingKey<N>>;
71
72    /// Inserts the verifying key for the given name.
73    /// The name can be a function name or a record name (for translation keys).
74    fn insert_verifying_key(
75        &self,
76        function_or_record_name: &Identifier<N>,
77        verifying_key: VerifyingKey<N>,
78    ) -> Result<()>;
79
80    /// Removes the verifying key for the given name.
81    /// The name can be a function name or a record name (for translation keys).
82    fn remove_verifying_key(&self, function_or_record_name: &Identifier<N>);
83
84    /// Checks that the given value matches the layout of the value type.
85    fn matches_value_type(&self, value: &Value<N>, value_type: &ValueType<N>) -> Result<()>;
86
87    /// Checks that the given stack value matches the layout of the register type.
88    fn matches_register_type(&self, stack_value: &Value<N>, register_type: &RegisterType<N>) -> Result<()>;
89
90    /// Checks that the given record matches the layout of the external record type.
91    fn matches_external_record(&self, record: &Record<N, Plaintext<N>>, locator: &Locator<N>) -> Result<()>;
92
93    /// Checks that the given record matches the layout of the record type.
94    fn matches_record(&self, record: &Record<N, Plaintext<N>>, record_name: &Identifier<N>) -> Result<()>;
95
96    /// Checks that the given plaintext matches the layout of the plaintext type.
97    fn matches_plaintext(&self, plaintext: &Plaintext<N>, plaintext_type: &PlaintextType<N>) -> Result<()>;
98
99    /// Checks that the given future matches the layout of the future type.
100    fn matches_future(&self, future: &Future<N>, locator: &Locator<N>) -> Result<()>;
101
102    /// Returns the program.
103    fn program(&self) -> &Program<N>;
104
105    /// Returns the program ID.
106    fn program_id(&self) -> &ProgramID<N>;
107
108    /// Returns the program address.
109    fn program_address(&self) -> &Address<N>;
110
111    /// Returns the program checksum.
112    fn program_checksum(&self) -> &[U8<N>; 32];
113
114    /// Returns the program checksum as a field element.
115    fn program_checksum_as_field(&self) -> Result<Field<N>>;
116
117    /// Returns the checksum of the program component (function, closure, or view) with the given name.
118    fn component_checksum(&self, name: &Identifier<N>) -> Result<&[U8<N>; 32]>;
119
120    /// Returns the program edition.
121    fn program_edition(&self) -> U16<N>;
122
123    /// Returns the number of amendments for the current program edition.
124    fn program_amendment_count(&self) -> u64;
125
126    /// Sets the number of amendments for the current program edition.
127    fn set_program_amendment_count(&mut self, program_amendment_count: u64);
128
129    /// Returns the program owner.
130    /// The program owner should only be set for programs that are deployed after `ConsensusVersion::V9` is active.
131    fn program_owner(&self) -> &Option<Address<N>>;
132
133    /// Sets the program owner.
134    fn set_program_owner(&mut self, program_owner: Option<Address<N>>);
135
136    /// Returns the external stack for the given program ID.
137    fn get_external_stack(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
138
139    /// Returns the external stack for the given program ID, without checking that:
140    ///
141    /// - The program ID is different from the current program ID.
142    /// - The program ID is imported by the current program.
143    ///
144    /// This function is only to be used for resolution during dynamic dispatch.
145    fn get_stack_global(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
146
147    /// Returns the function with the given function name.
148    fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>>;
149
150    /// Returns a reference to the function with the given function name.
151    fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>>;
152
153    /// Returns the minimum number of calls for the given function name.
154    /// Note: In a static call graph (no dynamic dispatch), the minimum is the actual count.
155    fn get_minimum_number_of_calls(&self, function_name: &Identifier<N>) -> Result<usize>;
156
157    /// Returns whether or not a function has a dynamic call in its execution.
158    fn contains_dynamic_call(&self, function_name: &Identifier<N>) -> Result<bool>;
159
160    /// Samples a value for the given value_type.
161    fn sample_value<R: Rng + CryptoRng>(
162        &self,
163        burner_address: &Address<N>,
164        value_type: &RegisterType<N>,
165        rng: &mut R,
166    ) -> Result<Value<N>>;
167
168    /// Returns a record for the given record name, with the given burner address and nonce.
169    fn sample_record<R: Rng + CryptoRng>(
170        &self,
171        burner_address: &Address<N>,
172        record_name: &Identifier<N>,
173        record_nonce: Group<N>,
174        rng: &mut R,
175    ) -> Result<Record<N, Plaintext<N>>>;
176
177    /// Returns a record for the given record name, deriving the nonce from tvk and index.
178    fn sample_record_using_tvk<R: Rng + CryptoRng>(
179        &self,
180        burner_address: &Address<N>,
181        record_name: &Identifier<N>,
182        tvk: Field<N>,
183        index: Field<N>,
184        rng: &mut R,
185    ) -> Result<Record<N, Plaintext<N>>>;
186
187    /// Evaluates a view function on this stack against the given finalize-store state.
188    ///
189    /// The caller (`Call::finalize`) loads operand values from the caller's registers and
190    /// passes them as `inputs`; this method runs the view body and returns its outputs. It
191    /// is the cross-crate hook that lets `Call::finalize` (in `snarkvm-synthesizer-program`)
192    /// dispatch view-call evaluation into `snarkvm-synthesizer-process` without depending on
193    /// concrete `Stack` / `FinalizeRegisters` types.
194    fn evaluate_view(
195        &self,
196        state: FinalizeGlobalState,
197        store: &dyn FinalizeStoreTrait<N>,
198        view_name: &Identifier<N>,
199        inputs: Vec<Value<N>>,
200    ) -> Result<Vec<Value<N>>>;
201}
202
203/// Are the two types either the same, or both structurally equivalent `PlaintextType`s?
204pub fn register_types_equivalent<N: Network>(
205    stack0: &impl StackTrait<N>,
206    type0: &RegisterType<N>,
207    stack1: &impl StackTrait<N>,
208    type1: &RegisterType<N>,
209) -> Result<bool> {
210    use RegisterType::*;
211    if let (Plaintext(plaintext0), Plaintext(plaintext1)) = (type0, type1) {
212        types_equivalent(stack0, plaintext0, stack1, plaintext1)
213    } else {
214        Ok(type0 == type1)
215    }
216}
217
218/// Determines whether two `PlaintextType` values are equivalent.
219///
220/// Equivalence of literals means they're the same type.
221///
222/// Equivalence of structs means they have the same local names (regardless of whether
223/// they're local or external), and their members have the same names and equivalent
224/// types in the same order, recursively.
225///
226/// Equivalence of arrays means they have the same length and their element types are
227/// equivalent.
228///
229/// This definition of equivalence was chosen to balance these concerns:
230///
231/// 1. All programs from before the existence of external structs will continue to work;
232///    thus it's necessary for a struct created from another program to be considered equivalent
233///    to a local one with the same name and structure, as in practice that was the behavior.
234/// 2. We don't want to allow a fork. Thus we do need to check names, not just structural
235///    equivalence - otherwise we could get a program deployable to a node which is using
236///    this check, but not deployable to a node running an earlier SnarkVM.
237///
238/// The stacks are passed because struct types need to access their stack to get their
239/// structure.
240pub fn types_equivalent<N: Network>(
241    stack0: &impl StackTrait<N>,
242    type0: &PlaintextType<N>,
243    stack1: &impl StackTrait<N>,
244    type1: &PlaintextType<N>,
245) -> Result<bool> {
246    use PlaintextType::*;
247
248    let struct_compare = |stack0, st0: &StructType<N>, stack1, st1: &StructType<N>| -> Result<bool> {
249        if st0.members().len() != st1.members().len() {
250            return Ok(false);
251        }
252
253        for ((name0, type0), (name1, type1)) in st0.members().iter().zip(st1.members()) {
254            if name0 != name1 || !types_equivalent(stack0, type0, stack1, type1)? {
255                return Ok(false);
256            }
257        }
258
259        Ok(true)
260    };
261
262    match (type0, type1) {
263        (Array(array0), Array(array1)) => Ok(array0.length() == array1.length()
264            && types_equivalent(stack0, array0.next_element_type(), stack1, array1.next_element_type())?),
265        (Literal(lit0), Literal(lit1)) => Ok(lit0 == lit1),
266        (Struct(id0), Struct(id1)) => {
267            if id0 != id1 {
268                return Ok(false);
269            }
270            let struct_type0 = stack0.program().get_struct(id0)?;
271            let struct_type1 = stack1.program().get_struct(id1)?;
272            struct_compare(stack0, struct_type0, stack1, struct_type1)
273        }
274        (ExternalStruct(loc0), ExternalStruct(loc1)) => {
275            if loc0.resource() != loc1.resource() {
276                return Ok(false);
277            }
278            let external_stack0 = stack0.get_external_stack(loc0.program_id())?;
279            let struct_type0 = external_stack0.program().get_struct(loc0.resource())?;
280            let external_stack1 = stack1.get_external_stack(loc1.program_id())?;
281            let struct_type1 = external_stack1.program().get_struct(loc1.resource())?;
282            struct_compare(&*external_stack0, struct_type0, &*external_stack1, struct_type1)
283        }
284        (ExternalStruct(loc), Struct(id)) => {
285            if loc.resource() != id {
286                return Ok(false);
287            }
288            let external_stack = stack0.get_external_stack(loc.program_id())?;
289            let struct_type0 = external_stack.program().get_struct(loc.resource())?;
290            let struct_type1 = stack1.program().get_struct(id)?;
291            struct_compare(&*external_stack, struct_type0, stack1, struct_type1)
292        }
293        (Struct(id), ExternalStruct(loc)) => {
294            if id != loc.resource() {
295                return Ok(false);
296            }
297            let struct_type0 = stack0.program().get_struct(id)?;
298            let external_stack = stack1.get_external_stack(loc.program_id())?;
299            let struct_type1 = external_stack.program().get_struct(loc.resource())?;
300            struct_compare(stack0, struct_type0, &*external_stack, struct_type1)
301        }
302        _ => Ok(false),
303    }
304}
305
306pub trait FinalizeRegistersState<N: Network>: RegistersTrait<N> {
307    /// Returns the global state for the finalize scope.
308    fn state(&self) -> &FinalizeGlobalState;
309
310    /// Returns the transition ID for the finalize scope, if one is associated with this scope.
311    /// View functions are externally-callable and have no associated transition, so this is
312    /// `None` on the view path; finalize and constructor scopes always have `Some(...)`.
313    fn transition_id(&self) -> Option<&N::TransitionID>;
314
315    /// Returns the function name for the finalize scope.
316    fn function_name(&self) -> &Identifier<N>;
317
318    /// Returns the nonce for the finalize registers, if one is associated with this scope.
319    /// `None` on the view path (no transition → no nonce); always `Some(...)` on finalize.
320    fn nonce(&self) -> Option<u64>;
321}
322
323pub trait RegistersSigner<N: Network>: RegistersTrait<N> {
324    /// Returns the transition signer.
325    fn signer(&self) -> Result<Address<N>>;
326
327    /// Sets the transition signer.
328    fn set_signer(&mut self, signer: Address<N>);
329
330    /// Returns the root transition view key.
331    fn root_tvk(&self) -> Result<Field<N>>;
332
333    /// Sets the root transition view key.
334    fn set_root_tvk(&mut self, root_tvk: Field<N>);
335
336    /// Returns the transition caller.
337    fn caller(&self) -> Result<Address<N>>;
338
339    /// Sets the transition caller.
340    fn set_caller(&mut self, caller: Address<N>);
341
342    /// Returns the transition view key.
343    fn tvk(&self) -> Result<Field<N>>;
344
345    /// Sets the transition view key.
346    fn set_tvk(&mut self, tvk: Field<N>);
347
348    /// Returns the request.
349    fn request(&self) -> Result<&Request<N>>;
350
351    /// Sets the request.
352    fn set_request(&mut self, request: Request<N>);
353}
354
355pub trait RegistersTrait<N: Network> {
356    /// Loads the value of a given operand.
357    ///
358    /// # Errors
359    /// This method should halt if the register locator is not found.
360    /// In the case of register members, this method should halt if the member is not found.
361    fn load(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Value<N>>;
362
363    /// Loads the literal of a given operand.
364    ///
365    /// # Errors
366    /// This method should halt if the given operand is not a literal.
367    /// This method should halt if the register locator is not found.
368    /// In the case of register members, this method should halt if the member is not found.
369    fn load_literal(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Literal<N>> {
370        match self.load(stack, operand)? {
371            Value::Plaintext(Plaintext::Literal(literal, ..)) => Ok(literal),
372            Value::Plaintext(Plaintext::Struct(..))
373            | Value::Plaintext(Plaintext::Array(..))
374            | Value::Record(..)
375            | Value::Future(..)
376            | Value::DynamicRecord(..)
377            | Value::DynamicFuture(..) => {
378                bail!("Operand must be a literal")
379            }
380        }
381    }
382
383    /// Loads the plaintext of a given operand.
384    ///
385    /// # Errors
386    /// This method should halt if the given operand is not a plaintext.
387    /// This method should halt if the register locator is not found.
388    /// In the case of register members, this method should halt if the member is not found.
389    fn load_plaintext(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Plaintext<N>> {
390        match self.load(stack, operand)? {
391            Value::Plaintext(plaintext) => Ok(plaintext),
392            Value::Record(..) | Value::Future(..) | Value::DynamicRecord(..) | Value::DynamicFuture(..) => {
393                bail!("Operand must be a plaintext")
394            }
395        }
396    }
397
398    /// Assigns the given value to the given register, assuming the register is not already assigned.
399    ///
400    /// # Errors
401    /// This method should halt if the given register is a register member.
402    /// This method should halt if the given register is an input register.
403    /// This method should halt if the register is already used.
404    fn store(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, stack_value: Value<N>) -> Result<()>;
405
406    /// Assigns the given literal to the given register, assuming the register is not already assigned.
407    ///
408    /// # Errors
409    /// This method should halt if the given register is a register member.
410    /// This method should halt if the given register is an input register.
411    /// This method should halt if the register is already used.
412    fn store_literal(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, literal: Literal<N>) -> Result<()> {
413        self.store(stack, register, Value::Plaintext(Plaintext::from(literal)))
414    }
415}
416
417/// This trait is intended to be implemented only by `snarkvm_synthesizer_process::Registers`.
418///
419/// We make it a trait only to avoid circular dependencies.
420pub trait RegistersCircuit<N: Network, A: circuit::Aleo<Network = N>> {
421    /// Returns the transition signer, as a circuit.
422    fn signer_circuit(&self) -> Result<circuit::Address<A>>;
423
424    /// Sets the transition signer, as a circuit.
425    fn set_signer_circuit(&mut self, signer_circuit: circuit::Address<A>);
426
427    /// Returns the root transition view key, as a circuit.
428    fn root_tvk_circuit(&self) -> Result<circuit::Field<A>>;
429
430    /// Sets the root transition view key, as a circuit.
431    fn set_root_tvk_circuit(&mut self, root_tvk_circuit: circuit::Field<A>);
432
433    /// Returns the transition caller, as a circuit.
434    fn caller_circuit(&self) -> Result<circuit::Address<A>>;
435
436    /// Sets the transition caller, as a circuit.
437    fn set_caller_circuit(&mut self, caller_circuit: circuit::Address<A>);
438
439    /// Returns the transition view key, as a circuit.
440    fn tvk_circuit(&self) -> Result<circuit::Field<A>>;
441
442    /// Sets the transition view key, as a circuit.
443    fn set_tvk_circuit(&mut self, tvk_circuit: circuit::Field<A>);
444
445    /// Loads the value of a given operand.
446    ///
447    /// # Errors
448    /// This method should halt if the register locator is not found.
449    /// In the case of register members, this method should halt if the member is not found.
450    fn load_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Value<A>>;
451
452    /// Loads the literal of a given operand.
453    ///
454    /// # Errors
455    /// This method should halt if the given operand is not a literal.
456    /// This method should halt if the register locator is not found.
457    /// In the case of register members, this method should halt if the member is not found.
458    fn load_literal_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Literal<A>> {
459        match self.load_circuit(stack, operand)? {
460            circuit::Value::Plaintext(circuit::Plaintext::Literal(literal, ..)) => Ok(literal),
461            circuit::Value::Plaintext(circuit::Plaintext::Struct(..))
462            | circuit::Value::Plaintext(circuit::Plaintext::Array(..))
463            | circuit::Value::Record(..)
464            | circuit::Value::Future(..)
465            | circuit::Value::DynamicRecord(..)
466            | circuit::Value::DynamicFuture(..) => bail!("Operand must be a literal"),
467        }
468    }
469
470    /// Loads the plaintext of a given operand.
471    ///
472    /// # Errors
473    /// This method should halt if the given operand is not a plaintext.
474    /// This method should halt if the register locator is not found.
475    /// In the case of register members, this method should halt if the member is not found.
476    fn load_plaintext_circuit(
477        &self,
478        stack: &impl StackTrait<N>,
479        operand: &Operand<N>,
480    ) -> Result<circuit::Plaintext<A>> {
481        match self.load_circuit(stack, operand)? {
482            circuit::Value::Plaintext(plaintext) => Ok(plaintext),
483            circuit::Value::Record(..)
484            | circuit::Value::Future(..)
485            | circuit::Value::DynamicRecord(..)
486            | circuit::Value::DynamicFuture(..) => bail!("Operand must be a plaintext"),
487        }
488    }
489
490    /// Assigns the given value to the given register, assuming the register is not already assigned.
491    ///
492    /// # Errors
493    /// This method should halt if the given register is a register member.
494    /// This method should halt if the given register is an input register.
495    /// This method should halt if the register is already used.
496    fn store_circuit(
497        &mut self,
498        stack: &impl StackTrait<N>,
499        register: &Register<N>,
500        stack_value: circuit::Value<A>,
501    ) -> Result<()>;
502
503    /// Assigns the given literal to the given register, assuming the register is not already assigned.
504    ///
505    /// # Errors
506    /// This method should halt if the given register is a register member.
507    /// This method should halt if the given register is an input register.
508    /// This method should halt if the register is already used.
509    fn store_literal_circuit(
510        &mut self,
511        stack: &impl StackTrait<N>,
512        register: &Register<N>,
513        literal: circuit::Literal<A>,
514    ) -> Result<()> {
515        self.store_circuit(stack, register, circuit::Value::Plaintext(circuit::Plaintext::from(literal)))
516    }
517}