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, 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 program edition.
118 fn program_edition(&self) -> U16<N>;
119
120 /// Returns the program owner.
121 /// The program owner should only be set for programs that are deployed after `ConsensusVersion::V9` is active.
122 fn program_owner(&self) -> &Option<Address<N>>;
123
124 /// Sets the program owner.
125 fn set_program_owner(&mut self, program_owner: Option<Address<N>>);
126
127 /// Returns the external stack for the given program ID.
128 fn get_external_stack(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
129
130 /// Returns the external stack for the given program ID, without checking that:
131 ///
132 /// - The program ID is different from the current program ID.
133 /// - The program ID is imported by the current program.
134 ///
135 /// This function is only to be used for resolution during dynamic dispatch.
136 fn get_stack_global(&self, program_id: &ProgramID<N>) -> Result<Arc<Self>>;
137
138 /// Returns the function with the given function name.
139 fn get_function(&self, function_name: &Identifier<N>) -> Result<Function<N>>;
140
141 /// Returns a reference to the function with the given function name.
142 fn get_function_ref(&self, function_name: &Identifier<N>) -> Result<&Function<N>>;
143
144 /// Returns the minimum number of calls for the given function name.
145 /// Note: In a static call graph (no dynamic dispatch), the minimum is the actual count.
146 fn get_minimum_number_of_calls(&self, function_name: &Identifier<N>) -> Result<usize>;
147
148 /// Returns whether or not a function has a dynamic call in its execution.
149 fn contains_dynamic_call(&self, function_name: &Identifier<N>) -> Result<bool>;
150
151 /// Samples a value for the given value_type.
152 fn sample_value<R: Rng + CryptoRng>(
153 &self,
154 burner_address: &Address<N>,
155 value_type: &RegisterType<N>,
156 rng: &mut R,
157 ) -> Result<Value<N>>;
158
159 /// Returns a record for the given record name, with the given burner address and nonce.
160 fn sample_record<R: Rng + CryptoRng>(
161 &self,
162 burner_address: &Address<N>,
163 record_name: &Identifier<N>,
164 record_nonce: Group<N>,
165 rng: &mut R,
166 ) -> Result<Record<N, Plaintext<N>>>;
167
168 /// Returns a record for the given record name, deriving the nonce from tvk and index.
169 fn sample_record_using_tvk<R: Rng + CryptoRng>(
170 &self,
171 burner_address: &Address<N>,
172 record_name: &Identifier<N>,
173 tvk: Field<N>,
174 index: Field<N>,
175 rng: &mut R,
176 ) -> Result<Record<N, Plaintext<N>>>;
177}
178
179/// Are the two types either the same, or both structurally equivalent `PlaintextType`s?
180pub fn register_types_equivalent<N: Network>(
181 stack0: &impl StackTrait<N>,
182 type0: &RegisterType<N>,
183 stack1: &impl StackTrait<N>,
184 type1: &RegisterType<N>,
185) -> Result<bool> {
186 use RegisterType::*;
187 if let (Plaintext(plaintext0), Plaintext(plaintext1)) = (type0, type1) {
188 types_equivalent(stack0, plaintext0, stack1, plaintext1)
189 } else {
190 Ok(type0 == type1)
191 }
192}
193
194/// Determines whether two `PlaintextType` values are equivalent.
195///
196/// Equivalence of literals means they're the same type.
197///
198/// Equivalence of structs means they have the same local names (regardless of whether
199/// they're local or external), and their members have the same names and equivalent
200/// types in the same order, recursively.
201///
202/// Equivalence of arrays means they have the same length and their element types are
203/// equivalent.
204///
205/// This definition of equivalence was chosen to balance these concerns:
206///
207/// 1. All programs from before the existence of external structs will continue to work;
208/// thus it's necessary for a struct created from another program to be considered equivalent
209/// to a local one with the same name and structure, as in practice that was the behavior.
210/// 2. We don't want to allow a fork. Thus we do need to check names, not just structural
211/// equivalence - otherwise we could get a program deployable to a node which is using
212/// this check, but not deployable to a node running an earlier SnarkVM.
213///
214/// The stacks are passed because struct types need to access their stack to get their
215/// structure.
216pub fn types_equivalent<N: Network>(
217 stack0: &impl StackTrait<N>,
218 type0: &PlaintextType<N>,
219 stack1: &impl StackTrait<N>,
220 type1: &PlaintextType<N>,
221) -> Result<bool> {
222 use PlaintextType::*;
223
224 let struct_compare = |stack0, st0: &StructType<N>, stack1, st1: &StructType<N>| -> Result<bool> {
225 if st0.members().len() != st1.members().len() {
226 return Ok(false);
227 }
228
229 for ((name0, type0), (name1, type1)) in st0.members().iter().zip(st1.members()) {
230 if name0 != name1 || !types_equivalent(stack0, type0, stack1, type1)? {
231 return Ok(false);
232 }
233 }
234
235 Ok(true)
236 };
237
238 match (type0, type1) {
239 (Array(array0), Array(array1)) => Ok(array0.length() == array1.length()
240 && types_equivalent(stack0, array0.next_element_type(), stack1, array1.next_element_type())?),
241 (Literal(lit0), Literal(lit1)) => Ok(lit0 == lit1),
242 (Struct(id0), Struct(id1)) => {
243 if id0 != id1 {
244 return Ok(false);
245 }
246 let struct_type0 = stack0.program().get_struct(id0)?;
247 let struct_type1 = stack1.program().get_struct(id1)?;
248 struct_compare(stack0, struct_type0, stack1, struct_type1)
249 }
250 (ExternalStruct(loc0), ExternalStruct(loc1)) => {
251 if loc0.resource() != loc1.resource() {
252 return Ok(false);
253 }
254 let external_stack0 = stack0.get_external_stack(loc0.program_id())?;
255 let struct_type0 = external_stack0.program().get_struct(loc0.resource())?;
256 let external_stack1 = stack1.get_external_stack(loc1.program_id())?;
257 let struct_type1 = external_stack1.program().get_struct(loc1.resource())?;
258 struct_compare(&*external_stack0, struct_type0, &*external_stack1, struct_type1)
259 }
260 (ExternalStruct(loc), Struct(id)) => {
261 if loc.resource() != id {
262 return Ok(false);
263 }
264 let external_stack = stack0.get_external_stack(loc.program_id())?;
265 let struct_type0 = external_stack.program().get_struct(loc.resource())?;
266 let struct_type1 = stack1.program().get_struct(id)?;
267 struct_compare(&*external_stack, struct_type0, stack1, struct_type1)
268 }
269 (Struct(id), ExternalStruct(loc)) => {
270 if id != loc.resource() {
271 return Ok(false);
272 }
273 let struct_type0 = stack0.program().get_struct(id)?;
274 let external_stack = stack1.get_external_stack(loc.program_id())?;
275 let struct_type1 = external_stack.program().get_struct(loc.resource())?;
276 struct_compare(stack0, struct_type0, &*external_stack, struct_type1)
277 }
278 _ => Ok(false),
279 }
280}
281
282pub trait FinalizeRegistersState<N: Network>: RegistersTrait<N> {
283 /// Returns the global state for the finalize scope.
284 fn state(&self) -> &FinalizeGlobalState;
285
286 /// Returns the transition ID for the finalize scope.
287 fn transition_id(&self) -> &N::TransitionID;
288
289 /// Returns the function name for the finalize scope.
290 fn function_name(&self) -> &Identifier<N>;
291
292 /// Returns the nonce for the finalize registers.
293 fn nonce(&self) -> u64;
294}
295
296pub trait RegistersSigner<N: Network>: RegistersTrait<N> {
297 /// Returns the transition signer.
298 fn signer(&self) -> Result<Address<N>>;
299
300 /// Sets the transition signer.
301 fn set_signer(&mut self, signer: Address<N>);
302
303 /// Returns the root transition view key.
304 fn root_tvk(&self) -> Result<Field<N>>;
305
306 /// Sets the root transition view key.
307 fn set_root_tvk(&mut self, root_tvk: Field<N>);
308
309 /// Returns the transition caller.
310 fn caller(&self) -> Result<Address<N>>;
311
312 /// Sets the transition caller.
313 fn set_caller(&mut self, caller: Address<N>);
314
315 /// Returns the transition view key.
316 fn tvk(&self) -> Result<Field<N>>;
317
318 /// Sets the transition view key.
319 fn set_tvk(&mut self, tvk: Field<N>);
320
321 /// Returns the request.
322 fn request(&self) -> Result<&Request<N>>;
323
324 /// Sets the request.
325 fn set_request(&mut self, request: Request<N>);
326}
327
328pub trait RegistersTrait<N: Network> {
329 /// Loads the value of a given operand.
330 ///
331 /// # Errors
332 /// This method should halt if the register locator is not found.
333 /// In the case of register members, this method should halt if the member is not found.
334 fn load(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Value<N>>;
335
336 /// Loads the literal of a given operand.
337 ///
338 /// # Errors
339 /// This method should halt if the given operand is not a literal.
340 /// This method should halt if the register locator is not found.
341 /// In the case of register members, this method should halt if the member is not found.
342 fn load_literal(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Literal<N>> {
343 match self.load(stack, operand)? {
344 Value::Plaintext(Plaintext::Literal(literal, ..)) => Ok(literal),
345 Value::Plaintext(Plaintext::Struct(..))
346 | Value::Plaintext(Plaintext::Array(..))
347 | Value::Record(..)
348 | Value::Future(..)
349 | Value::DynamicRecord(..)
350 | Value::DynamicFuture(..) => {
351 bail!("Operand must be a literal")
352 }
353 }
354 }
355
356 /// Loads the plaintext of a given operand.
357 ///
358 /// # Errors
359 /// This method should halt if the given operand is not a plaintext.
360 /// This method should halt if the register locator is not found.
361 /// In the case of register members, this method should halt if the member is not found.
362 fn load_plaintext(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<Plaintext<N>> {
363 match self.load(stack, operand)? {
364 Value::Plaintext(plaintext) => Ok(plaintext),
365 Value::Record(..) | Value::Future(..) | Value::DynamicRecord(..) | Value::DynamicFuture(..) => {
366 bail!("Operand must be a plaintext")
367 }
368 }
369 }
370
371 /// Assigns the given value to the given register, assuming the register is not already assigned.
372 ///
373 /// # Errors
374 /// This method should halt if the given register is a register member.
375 /// This method should halt if the given register is an input register.
376 /// This method should halt if the register is already used.
377 fn store(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, stack_value: Value<N>) -> Result<()>;
378
379 /// Assigns the given literal to the given register, assuming the register is not already assigned.
380 ///
381 /// # Errors
382 /// This method should halt if the given register is a register member.
383 /// This method should halt if the given register is an input register.
384 /// This method should halt if the register is already used.
385 fn store_literal(&mut self, stack: &impl StackTrait<N>, register: &Register<N>, literal: Literal<N>) -> Result<()> {
386 self.store(stack, register, Value::Plaintext(Plaintext::from(literal)))
387 }
388}
389
390/// This trait is intended to be implemented only by `snarkvm_synthesizer_process::Registers`.
391///
392/// We make it a trait only to avoid circular dependencies.
393pub trait RegistersCircuit<N: Network, A: circuit::Aleo<Network = N>> {
394 /// Returns the transition signer, as a circuit.
395 fn signer_circuit(&self) -> Result<circuit::Address<A>>;
396
397 /// Sets the transition signer, as a circuit.
398 fn set_signer_circuit(&mut self, signer_circuit: circuit::Address<A>);
399
400 /// Returns the root transition view key, as a circuit.
401 fn root_tvk_circuit(&self) -> Result<circuit::Field<A>>;
402
403 /// Sets the root transition view key, as a circuit.
404 fn set_root_tvk_circuit(&mut self, root_tvk_circuit: circuit::Field<A>);
405
406 /// Returns the transition caller, as a circuit.
407 fn caller_circuit(&self) -> Result<circuit::Address<A>>;
408
409 /// Sets the transition caller, as a circuit.
410 fn set_caller_circuit(&mut self, caller_circuit: circuit::Address<A>);
411
412 /// Returns the transition view key, as a circuit.
413 fn tvk_circuit(&self) -> Result<circuit::Field<A>>;
414
415 /// Sets the transition view key, as a circuit.
416 fn set_tvk_circuit(&mut self, tvk_circuit: circuit::Field<A>);
417
418 /// Loads the value of a given operand.
419 ///
420 /// # Errors
421 /// This method should halt if the register locator is not found.
422 /// In the case of register members, this method should halt if the member is not found.
423 fn load_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Value<A>>;
424
425 /// Loads the literal of a given operand.
426 ///
427 /// # Errors
428 /// This method should halt if the given operand is not a literal.
429 /// This method should halt if the register locator is not found.
430 /// In the case of register members, this method should halt if the member is not found.
431 fn load_literal_circuit(&self, stack: &impl StackTrait<N>, operand: &Operand<N>) -> Result<circuit::Literal<A>> {
432 match self.load_circuit(stack, operand)? {
433 circuit::Value::Plaintext(circuit::Plaintext::Literal(literal, ..)) => Ok(literal),
434 circuit::Value::Plaintext(circuit::Plaintext::Struct(..))
435 | circuit::Value::Plaintext(circuit::Plaintext::Array(..))
436 | circuit::Value::Record(..)
437 | circuit::Value::Future(..)
438 | circuit::Value::DynamicRecord(..)
439 | circuit::Value::DynamicFuture(..) => bail!("Operand must be a literal"),
440 }
441 }
442
443 /// Loads the plaintext of a given operand.
444 ///
445 /// # Errors
446 /// This method should halt if the given operand is not a plaintext.
447 /// This method should halt if the register locator is not found.
448 /// In the case of register members, this method should halt if the member is not found.
449 fn load_plaintext_circuit(
450 &self,
451 stack: &impl StackTrait<N>,
452 operand: &Operand<N>,
453 ) -> Result<circuit::Plaintext<A>> {
454 match self.load_circuit(stack, operand)? {
455 circuit::Value::Plaintext(plaintext) => Ok(plaintext),
456 circuit::Value::Record(..)
457 | circuit::Value::Future(..)
458 | circuit::Value::DynamicRecord(..)
459 | circuit::Value::DynamicFuture(..) => bail!("Operand must be a plaintext"),
460 }
461 }
462
463 /// Assigns the given value to the given register, assuming the register is not already assigned.
464 ///
465 /// # Errors
466 /// This method should halt if the given register is a register member.
467 /// This method should halt if the given register is an input register.
468 /// This method should halt if the register is already used.
469 fn store_circuit(
470 &mut self,
471 stack: &impl StackTrait<N>,
472 register: &Register<N>,
473 stack_value: circuit::Value<A>,
474 ) -> Result<()>;
475
476 /// Assigns the given literal to the given register, assuming the register is not already assigned.
477 ///
478 /// # Errors
479 /// This method should halt if the given register is a register member.
480 /// This method should halt if the given register is an input register.
481 /// This method should halt if the register is already used.
482 fn store_literal_circuit(
483 &mut self,
484 stack: &impl StackTrait<N>,
485 register: &Register<N>,
486 literal: circuit::Literal<A>,
487 ) -> Result<()> {
488 self.store_circuit(stack, register, circuit::Value::Plaintext(circuit::Plaintext::from(literal)))
489 }
490}