1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![allow(clippy::type_complexity)]
21
22extern crate snarkvm_circuit as circuit;
23extern crate snarkvm_console as console;
24
25mod cost;
26pub use cost::*;
27
28mod stack;
29pub use stack::*;
30
31mod trace;
32pub use trace::*;
33
34mod authorize;
35mod deploy;
36mod evaluate;
37mod execute;
38mod finalize;
39mod view;
40#[cfg(feature = "history")]
41pub use view::evaluate_view_at_height;
42mod verify_deployment;
43mod verify_execution;
44mod verify_fee;
45
46#[cfg(test)]
47mod tests;
48
49use console::{
50 account::PrivateKey,
51 network::prelude::*,
52 program::{
53 DynamicFuture,
54 Identifier,
55 Literal,
56 Locator,
57 OutputID,
58 Plaintext,
59 PlaintextType,
60 ProgramID,
61 Record,
62 Request,
63 Response,
64 Value,
65 ValueType,
66 compute_function_id,
67 },
68 types::{Field, U16, U64},
69};
70use snarkvm_algorithms::snark::varuna::VarunaVersion;
71use snarkvm_ledger_block::{Deployment, DeploymentVersion, Execution, Fee, Input, Output, Transaction, Transition};
72use snarkvm_ledger_store::{FinalizeStorage, FinalizeStore, atomic_batch_scope};
73use snarkvm_synthesizer_program::{
74 Branch,
75 CastType,
76 Command,
77 FinalizeGlobalState,
78 FinalizeOperation,
79 Function,
80 Instruction,
81 Operand,
82 Program,
83 StackTrait,
84};
85use snarkvm_synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey};
86use snarkvm_utilities::{defer, dev_println};
87
88use aleo_std::prelude::{finish, lap, timer};
89use indexmap::{IndexMap, IndexSet};
90
91#[cfg(feature = "locktick")]
92use locktick::{
93 LockGuard,
94 parking_lot::{Mutex, RwLock},
95};
96use parking_lot::MutexGuard;
97#[cfg(not(feature = "locktick"))]
98use parking_lot::{Mutex, RwLock};
99use std::{
100 collections::{HashMap, HashSet},
101 sync::Arc,
102};
103
104pub struct Process<N: Network> {
107 universal_srs: UniversalSRS<N>,
109 stacks: Arc<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
111 old_stacks: RwLock<IndexMap<ProgramID<N>, Option<Arc<Stack<N>>>>>,
113 lock: Mutex<()>,
116}
117
118pub struct ProcessExclusiveGuard<'a, N: Network> {
121 process: &'a Process<N>,
122 #[cfg(not(feature = "locktick"))]
123 _guard: MutexGuard<'a, ()>,
124 #[cfg(feature = "locktick")]
125 _guard: LockGuard<MutexGuard<'a, ()>>,
126}
127
128impl<'a, N: Network> std::ops::Deref for ProcessExclusiveGuard<'a, N> {
129 type Target = Process<N>;
130
131 fn deref(&self) -> &Self::Target {
132 self.process
133 }
134}
135
136impl<'a, N: Network> ProcessExclusiveGuard<'a, N> {
137 #[inline]
139 pub fn lock(&self) -> ! {
140 panic!("Attempted to lock `Process` from `ProcessExclusiveGuard`; this would deadlock")
141 }
142
143 #[inline]
147 pub fn add_stack(&self, stack: Stack<N>) -> Option<Arc<Stack<N>>> {
148 let program_id = *stack.program_id();
150 let stack = Arc::new(stack);
152 self.process.stacks.write().insert(program_id, stack)
154 }
155
156 #[inline]
161 pub fn stage_stack(&self, stack: Stack<N>) {
162 let program_id = *stack.program_id();
164 let stack = Arc::new(stack);
166 let old_stack = self.process.stacks.write().insert(program_id, stack);
169 let mut old_stacks = self.process.old_stacks.write();
170 if !old_stacks.contains_key(&program_id) {
171 old_stacks.insert(program_id, old_stack);
172 }
173 }
174
175 #[inline]
178 pub fn commit_stacks(&self) {
179 self.process.old_stacks.write().clear();
181 }
182
183 #[inline]
186 pub fn revert_stacks(&self) {
187 let mut stacks = self.process.stacks.write();
189 for (program_id, stack) in self.process.old_stacks.write().drain(..) {
190 if let Some(stack) = stack {
193 stacks.insert(program_id, stack);
194 } else {
195 stacks.shift_remove(&program_id);
196 }
197 }
198 }
199
200 #[inline]
204 pub fn add_program(&self, program: &Program<N>) -> Result<()> {
205 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
207 if program.id() != &credits_program_id {
209 self.add_stack(Stack::new(self.process, program)?);
210 }
211 Ok(())
212 }
213
214 #[inline]
218 pub fn add_program_with_edition(&self, program: &Program<N>, edition: u16) -> Result<()> {
219 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
221 if program.id() != &credits_program_id {
223 let stack = Stack::new_raw(self.process, program, edition)?;
224 stack.initialize_and_check(self.process)?;
225 self.add_stack(stack);
226 }
227 Ok(())
228 }
229
230 #[inline]
235 pub fn add_programs_with_editions(&self, programs: &[(Program<N>, u16)]) -> Result<()> {
236 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
238 defer! {
240 self.revert_stacks()
241 }
242 for (program, edition) in programs {
244 if program.id() != &credits_program_id {
245 self.stage_stack(Stack::new_raw(self.process, program, *edition)?)
246 }
247 }
248 for (program, _) in programs {
250 let stack = self.process.get_stack(program.id())?;
252 stack.initialize_and_check(self.process)?;
254 }
255 self.commit_stacks();
257 Ok(())
258 }
259
260 pub fn update_credits_verifying_keys(&self) -> Result<()> {
262 let credits = Program::<N>::credits()?;
264
265 for function_name in credits.functions().keys() {
267 self.process.remove_proving_key(credits.id(), function_name)?;
269 let verifying_key = N::get_credits_verifying_key(function_name.to_string())?;
271 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
275 self.process.insert_verifying_key(
277 credits.id(),
278 function_name,
279 VerifyingKey::new(verifying_key.clone(), num_variables),
280 )?;
281 }
282
283 Ok(())
284 }
285}
286
287impl<N: Network> Process<N> {
288 #[inline]
290 pub fn setup<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(rng: &mut R) -> Result<Self> {
291 let timer = timer!("Process:setup");
292
293 let process = Self {
295 universal_srs: UniversalSRS::load()?,
296 stacks: Default::default(),
297 old_stacks: Default::default(),
298 lock: Default::default(),
299 };
300 lap!(timer, "Initialize process");
301
302 let program = Program::credits()?;
304 lap!(timer, "Load credits program");
305
306 let stack = Stack::new(&process, &program)?;
308 lap!(timer, "Initialize stack");
309
310 for function_name in program.functions().keys() {
312 stack.synthesize_key::<A, _>(function_name, rng)?;
313 lap!(timer, "Synthesize circuit keys for {function_name}");
314 }
315 let rng = &mut rand::rng();
316 let credits_record_name = Identifier::<N>::from_str("credits").unwrap(); stack.synthesize_translation_key::<A, _>(&credits_record_name, rng)?;
318 lap!(timer, "Synthesize credits program keys");
319
320 process.lock().add_stack(stack);
322
323 finish!(timer);
324 Ok(process)
326 }
327
328 pub fn lock(&self) -> ProcessExclusiveGuard<'_, N> {
330 ProcessExclusiveGuard { process: self, _guard: self.lock.lock() }
331 }
332
333 pub fn mapping_types_exist(&self, program: &Program<N>) -> Result<()> {
335 for mapping in program.mappings().values() {
336 self.plaintext_exists(mapping.key().plaintext_type(), program)?;
337 self.plaintext_exists(mapping.value().plaintext_type(), program)?;
338 }
339 Ok(())
340 }
341
342 fn plaintext_exists(&self, type_: &PlaintextType<N>, program: &Program<N>) -> Result<()> {
344 match type_ {
345 PlaintextType::Literal(..) => Ok(()),
346 PlaintextType::Struct(struct_name) => {
347 ensure!(
349 program.get_struct(struct_name).is_ok(),
350 "Struct '{struct_name}' in '{}' is not defined.",
351 program.id()
352 );
353 Ok(())
354 }
355 PlaintextType::ExternalStruct(locator) => {
356 let stack = self.get_stack(locator.program_id())?;
357 ensure!(
358 stack.program().get_struct(locator.resource()).is_ok(),
359 "Struct '{}' in '{}' is not defined.",
360 locator.resource(),
361 stack.program().id(),
362 );
363 Ok(())
364 }
365 PlaintextType::Array(array_type) => self.plaintext_exists(array_type.base_element_type(), program),
366 }
367 }
368}
369
370impl<N: Network> Process<N> {
371 #[inline]
373 pub fn load() -> Result<Self> {
374 let timer = timer!("Process::load");
375
376 let process = Self {
378 universal_srs: UniversalSRS::load()?,
379 stacks: Default::default(),
380 old_stacks: Default::default(),
381 lock: Default::default(),
382 };
383 lap!(timer, "Initialize process");
384
385 let program = Program::credits()?;
387 lap!(timer, "Load credits program");
388
389 let stack = Stack::new(&process, &program)?;
391 lap!(timer, "Initialize stack");
392
393 for function_name in program.functions().keys() {
395 let verifying_key = N::get_credits_verifying_key(function_name.to_string())?;
397 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
401 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?;
403 lap!(timer, "Load verifying key for {function_name}");
404 }
405 lap!(timer, "Load circuit keys");
406
407 process.lock().add_stack(stack);
409
410 finish!(timer, "Process::load");
411 Ok(process)
413 }
414
415 #[inline]
417 pub fn load_v0() -> Result<Self> {
418 let timer = timer!("Process::load_v0");
419
420 let process = Self {
422 universal_srs: UniversalSRS::load()?,
423 stacks: Default::default(),
424 old_stacks: Default::default(),
425 lock: Default::default(),
426 };
427 lap!(timer, "Initialize process");
428
429 let program = Program::credits()?;
431 lap!(timer, "Load credits program");
432
433 let stack = Stack::new(&process, &program)?;
435 lap!(timer, "Initialize stack");
436
437 for function_name in program.functions().keys() {
439 let verifying_key = N::get_credits_v0_verifying_key(function_name.to_string())?;
441 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
445 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?;
447 lap!(timer, "Load verifying key for {function_name}");
448 }
449 lap!(timer, "Load circuit keys");
450
451 process.lock().add_stack(stack);
453
454 finish!(timer, "Process::load_v0");
455 Ok(process)
457 }
458
459 #[inline]
461 #[cfg(feature = "wasm")]
462 pub fn load_web() -> Result<Self> {
463 let process = Self {
465 universal_srs: UniversalSRS::load()?,
466 stacks: Default::default(),
467 old_stacks: Default::default(),
468 lock: Default::default(),
469 };
470
471 let program = Program::credits()?;
473
474 let stack = Stack::new(&process, &program)?;
476
477 process.lock().add_stack(stack);
479
480 Ok(process)
482 }
483
484 #[inline]
486 pub const fn universal_srs(&self) -> &UniversalSRS<N> {
487 &self.universal_srs
488 }
489
490 #[inline]
492 pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
493 self.stacks.read().contains_key(program_id)
494 }
495
496 #[inline]
498 pub fn program_ids(&self) -> Vec<ProgramID<N>> {
499 self.stacks.read().keys().copied().collect()
500 }
501
502 #[inline]
504 pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<Arc<Stack<N>>> {
505 let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?;
507 let stack = self
509 .stacks
510 .read()
511 .get(&program_id)
512 .ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))?
513 .clone();
514 ensure!(stack.program_id() == &program_id, "Expected program '{}', found '{program_id}'", stack.program_id());
516 Ok(stack)
518 }
519
520 #[inline]
522 pub fn get_latest_edition_for_program(&self, program_id: &ProgramID<N>) -> u16 {
523 self.get_stack(program_id).ok().map(|s| *s.program_edition()).unwrap_or(0u16)
524 }
525
526 #[inline]
528 pub fn get_proving_key(
529 &self,
530 program_id: impl TryInto<ProgramID<N>>,
531 function_name: impl TryInto<Identifier<N>>,
532 ) -> Result<ProvingKey<N>> {
533 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
535 self.get_stack(program_id)?.get_proving_key(&function_name)
537 }
538
539 #[inline]
541 pub fn get_verifying_key(
542 &self,
543 program_id: impl TryInto<ProgramID<N>>,
544 function_name: impl TryInto<Identifier<N>>,
545 ) -> Result<VerifyingKey<N>> {
546 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
548 self.get_stack(program_id)?.get_verifying_key(&function_name)
550 }
551
552 #[inline]
554 pub fn insert_proving_key(
555 &self,
556 program_id: &ProgramID<N>,
557 function_name: &Identifier<N>,
558 proving_key: ProvingKey<N>,
559 ) -> Result<()> {
560 self.get_stack(program_id)?.insert_proving_key(function_name, proving_key)
561 }
562
563 #[inline]
565 pub fn remove_proving_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> {
566 self.get_stack(program_id)?.remove_proving_key(function_name);
567 Ok(())
568 }
569
570 #[inline]
572 pub fn insert_verifying_key(
573 &self,
574 program_id: &ProgramID<N>,
575 function_name: &Identifier<N>,
576 verifying_key: VerifyingKey<N>,
577 ) -> Result<()> {
578 self.get_stack(program_id)?.insert_verifying_key(function_name, verifying_key)
579 }
580
581 #[inline]
583 pub fn remove_verifying_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> {
584 self.get_stack(program_id)?.remove_verifying_key(function_name);
585 Ok(())
586 }
587
588 #[inline]
590 pub fn synthesize_key<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
591 &self,
592 program_id: &ProgramID<N>,
593 function_name: &Identifier<N>,
594 rng: &mut R,
595 ) -> Result<()> {
596 self.get_stack(program_id)?.synthesize_key::<A, R>(function_name, rng)
598 }
599
600 #[inline]
602 pub fn synthesize_translation_key<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
603 &self,
604 program_id: &ProgramID<N>,
605 record_name: &Identifier<N>,
606 rng: &mut R,
607 ) -> Result<()> {
608 self.get_stack(program_id)?.synthesize_translation_key::<A, R>(record_name, rng)
609 }
610}
611
612#[cfg(test)]
613pub mod test_helpers {
614 use super::*;
615 use console::{account::PrivateKey, network::MainnetV0, program::Identifier};
616 use snarkvm_ledger_block::Transition;
617 use snarkvm_ledger_query::Query;
618 use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
619 use snarkvm_synthesizer_program::Program;
620
621 use aleo_std::StorageMode;
622 use std::sync::OnceLock;
623
624 type CurrentNetwork = MainnetV0;
625 type CurrentAleo = circuit::network::AleoV0;
626
627 pub fn get_execution(
629 process: &mut Process<CurrentNetwork>,
630 program: &Program<CurrentNetwork>,
631 function_name: &Identifier<CurrentNetwork>,
632 inputs: impl ExactSizeIterator<Item = impl TryInto<Value<CurrentNetwork>>>,
633 ) -> Execution<CurrentNetwork> {
634 let rng = &mut TestRng::default();
636
637 let private_key = PrivateKey::new(rng).unwrap();
639
640 if !process.contains_program(program.id()) {
642 process.lock().add_program(program).unwrap();
643 }
644
645 let authorization =
647 process.authorize::<CurrentAleo, _>(&private_key, program.id(), function_name, inputs, rng).unwrap();
648
649 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
651
652 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
654
655 trace.prepare(&snarkvm_ledger_query::Query::from(block_store)).unwrap();
657
658 let locator = format!("{:?}:{function_name:?}", program.id());
660
661 trace.prove_execution::<CurrentAleo, _>(&locator, VarunaVersion::V1, rng).unwrap()
663 }
664
665 pub fn sample_key() -> (Identifier<CurrentNetwork>, ProvingKey<CurrentNetwork>, VerifyingKey<CurrentNetwork>) {
666 static INSTANCE: OnceLock<(
667 Identifier<CurrentNetwork>,
668 ProvingKey<CurrentNetwork>,
669 VerifyingKey<CurrentNetwork>,
670 )> = OnceLock::new();
671 INSTANCE
672 .get_or_init(|| {
673 let (string, program) = Program::<CurrentNetwork>::parse(
675 r"
676program testing.aleo;
677
678function compute:
679 input r0 as u32.private;
680 input r1 as u32.public;
681 add r0 r1 into r2;
682 output r2 as u32.public;",
683 )
684 .unwrap();
685 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
686
687 let function_name = Identifier::from_str("compute").unwrap();
689
690 let rng = &mut TestRng::default();
692
693 let process = sample_process(&program);
695
696 process.synthesize_key::<CurrentAleo, _>(program.id(), &function_name, rng).unwrap();
698
699 let proving_key = process.get_proving_key(program.id(), function_name).unwrap();
701 let verifying_key = process.get_verifying_key(program.id(), function_name).unwrap();
702
703 (function_name, proving_key, verifying_key)
704 })
705 .clone()
706 }
707
708 pub(crate) fn sample_execution() -> Execution<CurrentNetwork> {
709 static INSTANCE: OnceLock<Execution<CurrentNetwork>> = OnceLock::new();
710 INSTANCE
711 .get_or_init(|| {
712 let (string, program) = Program::<CurrentNetwork>::parse(
714 r"
715program testing.aleo;
716
717function compute:
718 input r0 as u32.private;
719 input r1 as u32.public;
720 add r0 r1 into r2;
721 output r2 as u32.public;",
722 )
723 .unwrap();
724 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
725
726 let function_name = Identifier::from_str("compute").unwrap();
728
729 let rng = &mut TestRng::default();
731 let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
733
734 let block_store =
736 BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
737
738 let process = sample_process(&program);
740 let authorization = process
742 .authorize::<CurrentAleo, _>(
743 &caller_private_key,
744 program.id(),
745 function_name,
746 ["5u32", "10u32"].into_iter(),
747 rng,
748 )
749 .unwrap();
750 assert_eq!(authorization.len(), 1);
751 let (_response, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
753 assert_eq!(trace.transitions().len(), 1);
754
755 trace.prepare(&Query::from(block_store)).unwrap();
757 trace.prove_execution::<CurrentAleo, _>("testing", VarunaVersion::V1, rng).unwrap()
759 })
760 .clone()
761 }
762
763 pub fn sample_transition() -> Transition<CurrentNetwork> {
764 let mut execution = sample_execution();
766 assert!(!execution.is_empty());
768 execution.pop().unwrap()
770 }
771
772 pub(crate) fn sample_process(program: &Program<CurrentNetwork>) -> Process<CurrentNetwork> {
774 let process = Process::load().unwrap();
776 process.lock().add_program(program).unwrap();
778 process
780 }
781}