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