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 verify_deployment;
40mod verify_execution;
41mod verify_fee;
42
43#[cfg(test)]
44mod tests;
45
46use console::{
47 account::PrivateKey,
48 network::prelude::*,
49 program::{
50 DynamicFuture,
51 Identifier,
52 Literal,
53 Locator,
54 OutputID,
55 Plaintext,
56 PlaintextType,
57 ProgramID,
58 Record,
59 Request,
60 Response,
61 Value,
62 ValueType,
63 compute_function_id,
64 },
65 types::{Field, U16, U64},
66};
67use snarkvm_algorithms::snark::varuna::VarunaVersion;
68use snarkvm_ledger_block::{Deployment, DeploymentVersion, Execution, Fee, Input, Output, Transaction, Transition};
69use snarkvm_ledger_store::{FinalizeStorage, FinalizeStore, atomic_batch_scope};
70use snarkvm_synthesizer_program::{
71 Branch,
72 Command,
73 FinalizeGlobalState,
74 FinalizeOperation,
75 Function,
76 Instruction,
77 Program,
78 StackTrait,
79};
80use snarkvm_synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey};
81use snarkvm_utilities::{defer, dev_println};
82
83use aleo_std::prelude::{finish, lap, timer};
84use indexmap::IndexMap;
85#[cfg(feature = "locktick")]
86use locktick::parking_lot::RwLock;
87#[cfg(not(feature = "locktick"))]
88use parking_lot::RwLock;
89use std::{collections::HashMap, sync::Arc};
90
91#[derive(Clone)]
94pub struct Process<N: Network> {
95 universal_srs: UniversalSRS<N>,
97 stacks: Arc<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>,
99 old_stacks: Arc<RwLock<IndexMap<ProgramID<N>, Option<Arc<Stack<N>>>>>>,
101}
102
103impl<N: Network> Process<N> {
104 #[inline]
106 pub fn setup<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(rng: &mut R) -> Result<Self> {
107 let timer = timer!("Process:setup");
108
109 let mut process =
111 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() };
112 lap!(timer, "Initialize process");
113
114 let program = Program::credits()?;
116 lap!(timer, "Load credits program");
117
118 let stack = Stack::new(&process, &program)?;
120 lap!(timer, "Initialize stack");
121
122 for function_name in program.functions().keys() {
124 stack.synthesize_key::<A, _>(function_name, rng)?;
125 lap!(timer, "Synthesize circuit keys for {function_name}");
126 }
127 let rng = &mut rand::thread_rng();
128 let credits_record_name = Identifier::<N>::from_str("credits").unwrap(); stack.synthesize_translation_key::<A, _>(&credits_record_name, rng)?;
130 lap!(timer, "Synthesize credits program keys");
131
132 process.add_stack(stack);
134
135 finish!(timer);
136 Ok(process)
138 }
139
140 #[inline]
144 pub fn add_stack(&mut self, stack: Stack<N>) -> Option<Arc<Stack<N>>> {
145 let program_id = *stack.program_id();
147 let stack = Arc::new(stack);
149 self.stacks.write().insert(program_id, stack)
151 }
152
153 #[inline]
158 pub fn stage_stack(&self, stack: Stack<N>) {
159 let program_id = *stack.program_id();
161 let stack = Arc::new(stack);
163 let old_stack = self.stacks.write().insert(program_id, stack);
166 let mut old_stacks = self.old_stacks.write();
167 if !old_stacks.contains_key(&program_id) {
168 old_stacks.insert(program_id, old_stack);
169 }
170 }
171
172 #[inline]
175 pub fn commit_stacks(&self) {
176 self.old_stacks.write().clear();
178 }
179
180 #[inline]
183 pub fn revert_stacks(&self) {
184 for (program_id, stack) in self.old_stacks.write().drain(..) {
186 if let Some(stack) = stack {
189 self.stacks.write().insert(program_id, stack);
190 } else {
191 self.stacks.write().shift_remove(&program_id);
192 }
193 }
194 }
195
196 pub fn mapping_types_exist(&self, program: &Program<N>) -> Result<()> {
198 for mapping in program.mappings().values() {
199 self.plaintext_exists(mapping.key().plaintext_type(), program)?;
200 self.plaintext_exists(mapping.value().plaintext_type(), program)?;
201 }
202 Ok(())
203 }
204
205 fn plaintext_exists(&self, type_: &PlaintextType<N>, program: &Program<N>) -> Result<()> {
207 match type_ {
208 PlaintextType::Literal(..) => Ok(()),
209 PlaintextType::Struct(struct_name) => {
210 ensure!(
212 program.get_struct(struct_name).is_ok(),
213 "Struct '{struct_name}' in '{}' is not defined.",
214 program.id()
215 );
216 Ok(())
217 }
218 PlaintextType::ExternalStruct(locator) => {
219 let stack = self.get_stack(locator.program_id())?;
220 ensure!(
221 stack.program().get_struct(locator.resource()).is_ok(),
222 "Struct '{}' in '{}' is not defined.",
223 locator.resource(),
224 stack.program().id(),
225 );
226 Ok(())
227 }
228 PlaintextType::Array(array_type) => self.plaintext_exists(array_type.base_element_type(), program),
229 }
230 }
231}
232
233impl<N: Network> Process<N> {
234 #[inline]
236 pub fn load() -> Result<Self> {
237 let timer = timer!("Process::load");
238
239 let mut process =
241 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() };
242 lap!(timer, "Initialize process");
243
244 let program = Program::credits()?;
246 lap!(timer, "Load credits program");
247
248 let stack = Stack::new(&process, &program)?;
250 lap!(timer, "Initialize stack");
251
252 for function_name in program.functions().keys() {
254 let verifying_key = N::get_credits_verifying_key(function_name.to_string())?;
256 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
260 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?;
262 lap!(timer, "Load verifying key for {function_name}");
263 }
264 lap!(timer, "Load circuit keys");
265
266 process.add_stack(stack);
268
269 finish!(timer, "Process::load");
270 Ok(process)
272 }
273
274 #[inline]
276 pub fn load_v0() -> Result<Self> {
277 let timer = timer!("Process::load_v0");
278
279 let mut process =
281 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() };
282 lap!(timer, "Initialize process");
283
284 let program = Program::credits()?;
286 lap!(timer, "Load credits program");
287
288 let stack = Stack::new(&process, &program)?;
290 lap!(timer, "Initialize stack");
291
292 for function_name in program.functions().keys() {
294 let verifying_key = N::get_credits_v0_verifying_key(function_name.to_string())?;
296 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
300 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?;
302 lap!(timer, "Load verifying key for {function_name}");
303 }
304 lap!(timer, "Load circuit keys");
305
306 process.add_stack(stack);
308
309 finish!(timer, "Process::load_v0");
310 Ok(process)
312 }
313
314 #[inline]
316 #[cfg(feature = "wasm")]
317 pub fn load_web() -> Result<Self> {
318 let mut process =
320 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() };
321
322 let program = Program::credits()?;
324
325 let stack = Stack::new(&process, &program)?;
327
328 process.add_stack(stack);
330
331 Ok(process)
333 }
334
335 #[inline]
339 pub fn add_program(&mut self, program: &Program<N>) -> Result<()> {
340 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
342 if program.id() != &credits_program_id {
344 self.add_stack(Stack::new(self, program)?);
345 }
346 Ok(())
347 }
348
349 #[inline]
353 pub fn add_program_with_edition(&mut self, program: &Program<N>, edition: u16) -> Result<()> {
354 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
356 if program.id() != &credits_program_id {
358 let stack = Stack::new_raw(self, program, edition)?;
359 stack.initialize_and_check(self)?;
360 self.add_stack(stack);
361 }
362 Ok(())
363 }
364
365 #[inline]
370 pub fn add_programs_with_editions(&mut self, programs: &[(Program<N>, u16)]) -> Result<()> {
371 let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
373 defer! {
375 self.revert_stacks()
376 }
377 for (program, edition) in programs {
379 if program.id() != &credits_program_id {
380 self.stage_stack(Stack::new_raw(self, program, *edition)?)
381 }
382 }
383 for (program, _) in programs {
385 let stack = self.get_stack(program.id())?;
387 stack.initialize_and_check(self)?;
389 }
390 self.commit_stacks();
392 Ok(())
393 }
394
395 #[inline]
397 pub const fn universal_srs(&self) -> &UniversalSRS<N> {
398 &self.universal_srs
399 }
400
401 #[inline]
403 pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
404 self.stacks.read().contains_key(program_id)
405 }
406
407 #[inline]
409 pub fn program_ids(&self) -> Vec<ProgramID<N>> {
410 self.stacks.read().keys().copied().collect()
411 }
412
413 #[inline]
415 pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<Arc<Stack<N>>> {
416 let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?;
418 let stack = self
420 .stacks
421 .read()
422 .get(&program_id)
423 .ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))?
424 .clone();
425 ensure!(stack.program_id() == &program_id, "Expected program '{}', found '{program_id}'", stack.program_id());
427 Ok(stack)
429 }
430
431 #[inline]
433 pub fn get_proving_key(
434 &self,
435 program_id: impl TryInto<ProgramID<N>>,
436 function_name: impl TryInto<Identifier<N>>,
437 ) -> Result<ProvingKey<N>> {
438 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
440 self.get_stack(program_id)?.get_proving_key(&function_name)
442 }
443
444 #[inline]
446 pub fn get_verifying_key(
447 &self,
448 program_id: impl TryInto<ProgramID<N>>,
449 function_name: impl TryInto<Identifier<N>>,
450 ) -> Result<VerifyingKey<N>> {
451 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
453 self.get_stack(program_id)?.get_verifying_key(&function_name)
455 }
456
457 #[inline]
459 pub fn insert_proving_key(
460 &self,
461 program_id: &ProgramID<N>,
462 function_name: &Identifier<N>,
463 proving_key: ProvingKey<N>,
464 ) -> Result<()> {
465 self.get_stack(program_id)?.insert_proving_key(function_name, proving_key)
466 }
467
468 #[inline]
470 pub fn remove_proving_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> {
471 self.get_stack(program_id)?.remove_proving_key(function_name);
472 Ok(())
473 }
474
475 #[inline]
477 pub fn insert_verifying_key(
478 &self,
479 program_id: &ProgramID<N>,
480 function_name: &Identifier<N>,
481 verifying_key: VerifyingKey<N>,
482 ) -> Result<()> {
483 self.get_stack(program_id)?.insert_verifying_key(function_name, verifying_key)
484 }
485
486 #[inline]
488 pub fn remove_verifying_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> {
489 self.get_stack(program_id)?.remove_verifying_key(function_name);
490 Ok(())
491 }
492
493 #[inline]
495 pub fn synthesize_key<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
496 &self,
497 program_id: &ProgramID<N>,
498 function_name: &Identifier<N>,
499 rng: &mut R,
500 ) -> Result<()> {
501 self.get_stack(program_id)?.synthesize_key::<A, R>(function_name, rng)
503 }
504
505 #[inline]
507 pub fn synthesize_translation_key<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
508 &self,
509 program_id: &ProgramID<N>,
510 record_name: &Identifier<N>,
511 rng: &mut R,
512 ) -> Result<()> {
513 self.get_stack(program_id)?.synthesize_translation_key::<A, R>(record_name, rng)
514 }
515}
516
517#[cfg(test)]
518pub mod test_helpers {
519 use super::*;
520 use console::{account::PrivateKey, network::MainnetV0, program::Identifier};
521 use snarkvm_ledger_block::Transition;
522 use snarkvm_ledger_query::Query;
523 use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
524 use snarkvm_synthesizer_program::Program;
525
526 use aleo_std::StorageMode;
527 use std::sync::OnceLock;
528
529 type CurrentNetwork = MainnetV0;
530 type CurrentAleo = circuit::network::AleoV0;
531
532 pub fn get_execution(
534 process: &mut Process<CurrentNetwork>,
535 program: &Program<CurrentNetwork>,
536 function_name: &Identifier<CurrentNetwork>,
537 inputs: impl ExactSizeIterator<Item = impl TryInto<Value<CurrentNetwork>>>,
538 ) -> Execution<CurrentNetwork> {
539 let rng = &mut TestRng::default();
541
542 let private_key = PrivateKey::new(rng).unwrap();
544
545 if !process.contains_program(program.id()) {
547 process.add_program(program).unwrap();
548 }
549
550 let authorization =
552 process.authorize::<CurrentAleo, _>(&private_key, program.id(), function_name, inputs, rng).unwrap();
553
554 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
556
557 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
559
560 trace.prepare(&snarkvm_ledger_query::Query::from(block_store)).unwrap();
562
563 let locator = format!("{:?}:{function_name:?}", program.id());
565
566 trace.prove_execution::<CurrentAleo, _>(&locator, VarunaVersion::V1, rng).unwrap()
568 }
569
570 pub fn sample_key() -> (Identifier<CurrentNetwork>, ProvingKey<CurrentNetwork>, VerifyingKey<CurrentNetwork>) {
571 static INSTANCE: OnceLock<(
572 Identifier<CurrentNetwork>,
573 ProvingKey<CurrentNetwork>,
574 VerifyingKey<CurrentNetwork>,
575 )> = OnceLock::new();
576 INSTANCE
577 .get_or_init(|| {
578 let (string, program) = Program::<CurrentNetwork>::parse(
580 r"
581program testing.aleo;
582
583function compute:
584 input r0 as u32.private;
585 input r1 as u32.public;
586 add r0 r1 into r2;
587 output r2 as u32.public;",
588 )
589 .unwrap();
590 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
591
592 let function_name = Identifier::from_str("compute").unwrap();
594
595 let rng = &mut TestRng::default();
597
598 let process = sample_process(&program);
600
601 process.synthesize_key::<CurrentAleo, _>(program.id(), &function_name, rng).unwrap();
603
604 let proving_key = process.get_proving_key(program.id(), function_name).unwrap();
606 let verifying_key = process.get_verifying_key(program.id(), function_name).unwrap();
607
608 (function_name, proving_key, verifying_key)
609 })
610 .clone()
611 }
612
613 pub(crate) fn sample_execution() -> Execution<CurrentNetwork> {
614 static INSTANCE: OnceLock<Execution<CurrentNetwork>> = OnceLock::new();
615 INSTANCE
616 .get_or_init(|| {
617 let (string, program) = Program::<CurrentNetwork>::parse(
619 r"
620program testing.aleo;
621
622function compute:
623 input r0 as u32.private;
624 input r1 as u32.public;
625 add r0 r1 into r2;
626 output r2 as u32.public;",
627 )
628 .unwrap();
629 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
630
631 let function_name = Identifier::from_str("compute").unwrap();
633
634 let rng = &mut TestRng::default();
636 let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
638
639 let block_store =
641 BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
642
643 let process = sample_process(&program);
645 let authorization = process
647 .authorize::<CurrentAleo, _>(
648 &caller_private_key,
649 program.id(),
650 function_name,
651 ["5u32", "10u32"].into_iter(),
652 rng,
653 )
654 .unwrap();
655 assert_eq!(authorization.len(), 1);
656 let (_response, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
658 assert_eq!(trace.transitions().len(), 1);
659
660 trace.prepare(&Query::from(block_store)).unwrap();
662 trace.prove_execution::<CurrentAleo, _>("testing", VarunaVersion::V1, rng).unwrap()
664 })
665 .clone()
666 }
667
668 pub fn sample_transition() -> Transition<CurrentNetwork> {
669 let mut execution = sample_execution();
671 assert!(!execution.is_empty());
673 execution.pop().unwrap()
675 }
676
677 pub(crate) fn sample_process(program: &Program<CurrentNetwork>) -> Process<CurrentNetwork> {
679 let mut process = Process::load().unwrap();
681 process.add_program(program).unwrap();
683 process
685 }
686}