Skip to main content

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