1use super::*;
17use console::program::{FinalizeType, Future, Identifier, Register};
18use snarkvm_synthesizer_error::{FinalizeError, IndexedFinalizeError, IntoIndexedFinalize, indexed_finalize_bail};
19use snarkvm_synthesizer_program::{Await, FinalizeRegistersState, FinalizeStoreTrait, Operand, RegistersTrait};
20use snarkvm_utilities::try_vm_runtime;
21
22use std::collections::HashSet;
23
24type TotalAwaits = usize;
25
26impl<'a, N: Network> ProcessExclusiveGuard<'a, N> {
27 #[inline]
31 pub fn finalize_deployment<P: FinalizeStorage<N>>(
32 &self,
33 state: FinalizeGlobalState,
34 store: &FinalizeStore<N, P>,
35 deployment: &Deployment<N>,
36 fee: &Fee<N>,
37 ) -> Result<(Stack<N>, Vec<FinalizeOperation<N>>), IndexedFinalizeError<N, Command<N>>> {
38 let timer = timer!("Process::finalize_deployment");
39
40 let deploy_program_id = deployment.program().id();
42
43 let version = deployment.version()?;
45
46 match version {
48 DeploymentVersion::V1 | DeploymentVersion::V2 => {
49 let mut stack = Stack::new(self.process, deployment.program()).into_indexed(
51 Some((*deploy_program_id, deployment.edition())),
52 None,
53 None::<(usize, Command<N>)>,
54 )?;
55 lap!(timer, "Compute the stack");
56
57 stack.set_program_owner(deployment.program_owner());
59
60 for (function_name, (verifying_key, _)) in deployment.verifying_keys() {
62 stack.insert_verifying_key(function_name, verifying_key.clone()).into_indexed(
63 Some((*deploy_program_id, deployment.edition())),
64 None,
65 None::<(usize, Command<N>)>,
66 )?;
67 }
68 lap!(timer, "Insert the verifying keys");
69
70 let mappings = match deployment.edition().is_zero() {
72 true => deployment.program().mappings().values().collect::<Vec<_>>(),
73 false => {
74 let existing_stack = self.process.get_stack(deployment.program_id()).into_indexed(
76 Some((*deploy_program_id, deployment.edition())),
77 None,
78 None::<(usize, Command<N>)>,
79 )?;
80 let existing_mappings = existing_stack.program().mappings();
82 let mut new_mappings = Vec::new();
84 for mapping in deployment.program().mappings().values() {
85 if !existing_mappings.contains_key(mapping.name()) {
86 new_mappings.push(mapping);
87 }
88 }
89 new_mappings
90 }
91 };
92 lap!(timer, "Retrieve the mappings to initialize");
93
94 atomic_batch_scope!(store, IndexedFinalizeError::<N, Command<N>>, {
96 let mut finalize_operations = Vec::with_capacity(deployment.program().mappings().len());
98
99 let fee_stack = self.process.get_stack(fee.program_id()).into_indexed(
103 Some((*fee.program_id(), self.get_latest_edition_for_program(fee.program_id()))),
104 Some(*fee.function_name()),
105 None::<(usize, Command<N>)>,
106 )?;
107 finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
109 lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
110
111 let program_id = deployment.program_id();
115 for mapping in mappings {
117 finalize_operations.push(store.initialize_mapping(*program_id, *mapping.name()).into_indexed(
119 Some((*program_id, deployment.edition())),
120 None,
121 None::<(usize, Command<N>)>,
122 )?);
123 }
124 lap!(timer, "Initialize the program mappings");
125
126 if deployment.program().contains_constructor() {
129 let operations = finalize_constructor(state, store, &stack, N::TransitionID::default())?;
130 finalize_operations.extend(operations);
131 lap!(timer, "Execute the constructor");
132 }
133
134 finish!(timer, "Finished finalizing the deployment");
135 Ok((stack, finalize_operations))
137 })
138 }
139 DeploymentVersion::V3 => {
140 if deployment.program_id() == &ProgramID::credits() {
142 return Err(
143 anyhow!("The 'credits.aleo' program cannot be deployed with DeploymentVersion::V3").into()
144 );
145 }
146
147 let existing_stack = self.process.get_stack(deployment.program_id())?;
149 let amendment_count = existing_stack
151 .program_amendment_count()
152 .checked_add(1)
153 .ok_or_else(|| anyhow!("Overflow while incrementing the program amendment count"))?;
154
155 let mut stack = Stack::new_raw(self.process, deployment.program(), *existing_stack.program_edition())?;
159 stack.initialize_and_check(self.process)?;
160 lap!(timer, "Compute the stack");
161
162 stack.set_program_amendment_count(amendment_count);
164 stack.set_program_owner(*existing_stack.program_owner());
166
167 for (name, (verifying_key, _)) in deployment.verifying_keys() {
169 stack.insert_verifying_key(name, verifying_key.clone())?;
170 }
171 lap!(timer, "Insert the verifying keys");
172
173 atomic_batch_scope!(store, IndexedFinalizeError::<N, Command<N>>, {
175 let mut finalize_operations = Vec::new();
176
177 let fee_stack = self.process.get_stack(fee.program_id())?;
179 finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
181 lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
182
183 finish!(timer, "Finished finalizing the V3 deployment");
184 Ok((stack, finalize_operations))
185 })
186 }
187 }
188 }
189
190 #[inline]
194 pub fn finalize_execution<P: FinalizeStorage<N>>(
195 &self,
196 state: FinalizeGlobalState,
197 store: &FinalizeStore<N, P>,
198 execution: &Execution<N>,
199 fee: Option<&Fee<N>>,
200 ) -> Result<Vec<FinalizeOperation<N>>, IndexedFinalizeError<N, Command<N>>> {
201 let timer = timer!("Program::finalize_execution");
202
203 if execution.is_empty() {
205 indexed_finalize_bail!(None, None, "There are no transitions in the execution");
206 }
207
208 let transition = execution.peek().into_indexed(None, None, None::<(usize, Command<N>)>)?;
211 let transition_program_id = *transition.program_id();
213 let transition_function_name = *transition.function_name();
214 let stack = self.process.get_stack(transition.program_id()).into_indexed(
216 Some((transition_program_id, self.get_latest_edition_for_program(&transition_program_id))),
217 Some(transition_function_name),
218 None::<(usize, Command<N>)>,
219 )?;
220 let minimum_number_of_calls = stack.get_minimum_number_of_calls(transition.function_name()).into_indexed(
222 Some((transition_program_id, *stack.program_edition())),
223 Some(transition_function_name),
224 None::<(usize, Command<N>)>,
225 )?;
226 if stack.contains_dynamic_call(transition.function_name())? {
230 if minimum_number_of_calls > execution.len() {
231 indexed_finalize_bail!(
232 Some((transition_program_id, *stack.program_edition())),
233 Some(transition_function_name),
234 "The number of transitions in the execution is incorrect. \
235 Expected at least {minimum_number_of_calls}, but found {}",
236 execution.len()
237 );
238 }
239 } else if minimum_number_of_calls != execution.len() {
240 indexed_finalize_bail!(
241 Some((transition_program_id, *stack.program_edition())),
242 Some(transition_function_name),
243 "The number of transitions in the execution is incorrect. \
244 Expected {minimum_number_of_calls}, but found {}",
245 execution.len()
246 );
247 }
248 lap!(timer, "Verify the number of transitions");
249
250 let dynamic_future_to_future: HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>> = execution
255 .transitions()
256 .filter_map(|transition| {
257 transition.outputs().last().and_then(|output| output.future()).and_then(|future| {
258 let dynamic_future = DynamicFuture::from_future(future).ok()?;
259 let key = (
260 *dynamic_future.program_name(),
261 *dynamic_future.program_network(),
262 *dynamic_future.function_name(),
263 *dynamic_future.checksum(),
264 );
265 Some((key, future))
266 })
267 })
268 .collect();
269
270 let consensus_version = N::CONSENSUS_VERSION(state.block_height()).into_indexed(
272 Some((transition_program_id, *stack.program_edition())),
273 Some(transition_function_name),
274 None::<(usize, Command<N>)>,
275 )?;
276 let call_graph = match (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
277 true => {
278 let mut execution_stacks = indexmap::IndexMap::new();
279 for transition in execution.transitions() {
280 execution_stacks.insert(*transition.program_id(), self.process.get_stack(transition.program_id())?);
281 }
282 Process::construct_call_graph(execution.transitions(), &execution_stacks).into_indexed(
283 Some((transition_program_id, *stack.program_edition())),
284 Some(transition_function_name),
285 None::<(usize, Command<N>)>,
286 )?
287 }
288 false => HashMap::new(),
290 };
291
292 atomic_batch_scope!(store, IndexedFinalizeError::<N, Command<N>>, {
293 let (mut finalize_operations, total_awaits) =
297 finalize_transition(state, store, &stack, transition, call_graph, dynamic_future_to_future)?;
298
299 if consensus_version >= ConsensusVersion::V15 {
300 let total_futures = execution
304 .transitions()
305 .filter(|t| t.outputs().last().and_then(|output| output.future()).is_some())
306 .count();
307 let expected_total_awaits = total_futures.saturating_sub(1);
308 if total_awaits != expected_total_awaits {
309 indexed_finalize_bail!(
310 Some((transition_program_id, *stack.program_edition())),
311 Some(transition_function_name),
312 "The number of 'await' calls during finalization is incorrect. \
313 Expected {expected_total_awaits}, but found {total_awaits}"
314 );
315 }
316 }
317
318 if let Some(fee) = fee {
320 let fee_stack = self.process.get_stack(fee.program_id()).into_indexed(
322 Some((*fee.program_id(), self.get_latest_edition_for_program(fee.program_id()))),
323 Some(*fee.function_name()),
324 None::<(usize, Command<N>)>,
325 )?;
326 finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
328 lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
329 }
330
331 finish!(timer);
332 Ok(finalize_operations)
334 })
335 }
336
337 #[inline]
341 pub fn finalize_fee<P: FinalizeStorage<N>>(
342 &self,
343 state: FinalizeGlobalState,
344 store: &FinalizeStore<N, P>,
345 fee: &Fee<N>,
346 ) -> Result<Vec<FinalizeOperation<N>>, IndexedFinalizeError<N, Command<N>>> {
347 let timer = timer!("Program::finalize_fee");
348
349 atomic_batch_scope!(store, IndexedFinalizeError::<N, Command<N>>, {
350 let stack = self.process.get_stack(fee.program_id()).into_indexed(
352 Some((*fee.program_id(), self.get_latest_edition_for_program(fee.program_id()))),
353 Some(*fee.function_name()),
354 None::<(usize, Command<N>)>,
355 )?;
356 let result = finalize_fee_transition(state, store, &stack, fee);
358 finish!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
359 result
361 })
362 }
363}
364
365fn finalize_fee_transition<N: Network, P: FinalizeStorage<N>>(
367 state: FinalizeGlobalState,
368 store: &FinalizeStore<N, P>,
369 stack: &Arc<Stack<N>>,
370 fee: &Fee<N>,
371) -> Result<Vec<FinalizeOperation<N>>, IndexedFinalizeError<N, Command<N>>> {
372 let consensus_version = N::CONSENSUS_VERSION(state.block_height()).into_indexed(
374 Some((*fee.program_id(), *stack.program_edition())),
375 Some(*fee.function_name()),
376 None::<(usize, Command<N>)>,
377 )?;
378 let call_graph = match (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
379 true => HashMap::from([(*fee.transition_id(), Vec::new())]),
380 false => HashMap::new(),
382 };
383
384 let (finalize_operations, total_awaits) =
386 finalize_transition(state, store, stack, fee, call_graph, Default::default())?;
387 if consensus_version >= ConsensusVersion::V15 && total_awaits != 0 {
389 indexed_finalize_bail!(
390 Some((*fee.program_id(), *stack.program_edition())),
391 Some(*fee.function_name()),
392 "Fees must not have any awaits"
393 );
394 }
395 Ok(finalize_operations)
396}
397
398fn finalize_constructor<N: Network, P: FinalizeStorage<N>>(
400 state: FinalizeGlobalState,
401 store: &FinalizeStore<N, P>,
402 stack: &Stack<N>,
403 transition_id: N::TransitionID,
404) -> Result<Vec<FinalizeOperation<N>>, IndexedFinalizeError<N, Command<N>>> {
405 let program_id = stack.program_id();
407 let edition = stack.program_edition();
408 let resource = Identifier::from_str("constructor")?;
409 dev_println!("Finalizing constructor for {}...", stack.program_id());
410
411 let mut finalize_operations = Vec::new();
413
414 let nonce = 0;
417
418 let Some(constructor) = stack.program().constructor() else {
420 return Ok(finalize_operations);
421 };
422
423 let constructor_types = match stack.get_constructor_types() {
425 Ok(types) => types.clone(),
426 Err(error) => {
427 indexed_finalize_bail!(
428 Some((*program_id, *edition)),
429 Some(resource),
430 "Failed to get constructor types - {error}"
431 )
432 }
433 };
434
435 let mut registers =
437 FinalizeRegisters::new(state, Some(transition_id), *program_id.name(), constructor_types, Some(nonce));
438
439 let scope_name = Identifier::<N>::from_str("constructor")?;
441
442 let mut counter = 0;
444
445 while counter < constructor.commands().len() {
447 let command = &constructor.commands()[counter];
449 match &command {
451 Command::Await(_) => {
452 indexed_finalize_bail!(
453 Some((*program_id, *edition)),
454 Some(resource),
455 counter,
456 command.clone(),
457 "Cannot `await` a Future in a constructor"
458 )
459 }
460 _ => finalize_command_except_await(
461 Some((*program_id, *edition)),
462 Some(resource),
463 store,
464 stack,
465 &mut registers,
466 constructor.positions(),
467 command,
468 &mut counter,
469 &mut finalize_operations,
470 &scope_name,
471 )?,
472 };
473 }
474
475 Ok(finalize_operations)
477}
478
479fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
481 state: FinalizeGlobalState,
482 store: &FinalizeStore<N, P>,
483 stack: &Arc<Stack<N>>,
484 transition: &Transition<N>,
485 call_graph: HashMap<N::TransitionID, Vec<N::TransitionID>>,
486 dynamic_future_to_future: HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>>,
487) -> Result<(Vec<FinalizeOperation<N>>, TotalAwaits), IndexedFinalizeError<N, Command<N>>> {
488 let program_id = transition.program_id();
490 let function_name = transition.function_name();
492
493 dev_println!("Finalizing transition for {program_id}/{function_name}...");
494 debug_assert_eq!(stack.program_id(), transition.program_id());
495
496 let future = match transition.outputs().last().and_then(|output| output.future()) {
498 Some(future) => future,
499 _ => return Ok((Vec::new(), 0)),
500 };
501
502 if future.program_id() != program_id || future.function_name() != function_name {
504 indexed_finalize_bail!(
505 Some((*program_id, *stack.program_edition())),
506 Some(*function_name),
507 "The program ID and function name of the future do not match the transition"
508 );
509 }
510
511 let mut finalize_operations = Vec::new();
513
514 let mut states = Vec::new();
516
517 let mut nonce = 0;
520 let is_dynamic = false;
522
523 states.push(initialize_finalize_state(state, future, stack, *transition.id(), nonce, is_dynamic).into_indexed(
525 Some((*program_id, *stack.program_edition())),
526 Some(*function_name),
527 None::<(usize, Command<N>)>,
528 )?);
529
530 let mut total_awaits: TotalAwaits = 0;
532
533 'outer: while let Some(FinalizeState { mut counter, mut registers, stack, mut call_counter, mut awaited }) =
535 states.pop()
536 {
537 let finalize_program_id = *stack.program_id();
539 let finalize_edition = *stack.program_edition();
540 let finalize_resource = *registers.function_name();
541 let Some(finalize) = stack
543 .get_function_ref(registers.function_name())
544 .into_indexed(
545 Some((finalize_program_id, finalize_edition)),
546 Some(finalize_resource),
547 None::<(usize, Command<N>)>,
548 )?
549 .finalize_logic()
550 else {
551 indexed_finalize_bail!(
552 Some((finalize_program_id, finalize_edition)),
553 Some(finalize_resource),
554 "The function '{finalize_program_id}/{finalize_resource}' does not have an associated finalize scope",
555 )
556 };
557 let scope_name = *registers.function_name();
559 while counter < finalize.commands().len() {
561 let command = &finalize.commands()[counter];
563 match &command {
565 Command::Await(await_) => {
566 if let Register::Access(_, _) = await_.register() {
568 indexed_finalize_bail!(
569 Some((finalize_program_id, finalize_edition)),
570 Some(finalize_resource),
571 "The 'await' register must be a locator"
572 )
573 };
574 if awaited.contains(await_.register()) {
576 indexed_finalize_bail!(
577 Some((finalize_program_id, finalize_edition)),
578 Some(finalize_resource),
579 counter,
580 command.clone(),
581 "The future register '{}' has already been awaited",
582 await_.register()
583 );
584 }
585 let consensus_version = N::CONSENSUS_VERSION(state.block_height()).into_indexed(
589 Some((finalize_program_id, finalize_edition)),
590 Some(finalize_resource),
591 Some((counter, command.clone())),
592 )?;
593 let transition_id = if (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
594 let transition_id = registers
599 .transition_id()
600 .ok_or_else(|| anyhow!("Cannot resolve a child transition ID without a transition ID"))?;
601 match call_graph.get(transition_id) {
603 Some(transitions) => match transitions.get(call_counter) {
604 Some(transition_id) => *transition_id,
605 None => indexed_finalize_bail!(
606 Some((finalize_program_id, finalize_edition)),
607 Some(finalize_resource),
608 counter,
609 command.clone(),
610 "Child transition ID not found."
611 ),
612 },
613 None => indexed_finalize_bail!(
614 Some((finalize_program_id, finalize_edition)),
615 Some(finalize_resource),
616 counter,
617 command.clone(),
618 "Transition ID '{transition_id}' not found in call graph"
619 ),
620 }
621 } else {
622 *transition.id()
623 };
624
625 nonce += 1;
627
628 let callee_state = match try_vm_runtime!(|| setup_await(
630 state,
631 await_,
632 &stack,
633 ®isters,
634 transition_id,
635 nonce,
636 &dynamic_future_to_future,
637 )) {
638 Ok(Ok(callee_state)) => callee_state,
639 Ok(Err(error)) => indexed_finalize_bail!(
641 Some((finalize_program_id, finalize_edition)),
642 Some(finalize_resource),
643 counter,
644 command.clone(),
645 "'finalize' failed to evaluate command: {error}"
646 ),
647 Err(_) => indexed_finalize_bail!(
649 Some((finalize_program_id, finalize_edition)),
650 Some(finalize_resource),
651 counter,
652 command.clone(),
653 "'finalize' failed to evaluate command"
654 ),
655 };
656
657 call_counter += 1;
659 total_awaits += 1;
661 counter += 1;
663 awaited.insert(await_.register().clone());
665
666 let caller_state = FinalizeState { counter, registers, stack, call_counter, awaited };
668
669 states.push(caller_state);
671 states.push(callee_state);
673
674 continue 'outer;
675 }
676 _ => finalize_command_except_await(
677 Some((finalize_program_id, finalize_edition)),
678 Some(finalize_resource),
679 store,
680 stack.deref(),
681 &mut registers,
682 finalize.positions(),
683 command,
684 &mut counter,
685 &mut finalize_operations,
686 &scope_name,
687 )?,
688 };
689 }
690 let mut unawaited = Vec::new();
692 for input in finalize.inputs() {
693 if matches!(input.finalize_type(), FinalizeType::Future(_) | FinalizeType::DynamicFuture)
694 && !awaited.contains(input.register())
695 {
696 unawaited.push(input.register().clone());
697 }
698 }
699 if !unawaited.is_empty() {
700 indexed_finalize_bail!(
701 Some((finalize_program_id, finalize_edition)),
702 Some(finalize_resource),
703 "The following future registers have not been awaited: {}",
704 unawaited.iter().map(|r| r.to_string()).collect::<Vec<_>>().join(", ")
705 );
706 }
707 }
708
709 Ok((finalize_operations, total_awaits))
711}
712
713struct FinalizeState<N: Network> {
715 counter: usize,
717 registers: FinalizeRegisters<N>,
719 stack: Arc<Stack<N>>,
721 call_counter: usize,
723 awaited: HashSet<Register<N>>,
725}
726
727fn initialize_finalize_state<N: Network>(
729 state: FinalizeGlobalState,
730 future: &Future<N>,
731 stack: &Arc<Stack<N>>,
732 transition_id: N::TransitionID,
733 nonce: u64,
734 is_dynamic: bool,
735) -> Result<FinalizeState<N>> {
736 let stack = match (stack.program_id() == future.program_id(), is_dynamic) {
738 (true, _) => stack.clone(),
739 (false, true) => stack.get_stack_global(future.program_id())?,
740 (false, false) => stack.get_external_stack(future.program_id())?,
741 };
742 let Some(finalize) = stack.get_function_ref(future.function_name())?.finalize_logic() else {
744 bail!(
745 "The function '{}/{}' does not have an associated finalize scope",
746 future.program_id(),
747 future.function_name()
748 )
749 };
750 let mut registers = FinalizeRegisters::new(
752 state,
753 Some(transition_id),
754 *future.function_name(),
755 stack.get_finalize_types(future.function_name())?.clone(),
756 Some(nonce),
757 );
758
759 finalize.inputs().iter().map(|i| i.register()).zip_eq(future.arguments().iter()).try_for_each(
762 |(register, input)| {
763 registers.store(stack.deref(), register, Value::from(input))
765 },
766 )?;
767
768 Ok(FinalizeState { counter: 0, registers, stack, call_counter: 0, awaited: Default::default() })
769}
770
771#[inline]
778pub(crate) fn finalize_command_except_await<N: Network>(
779 program_id: Option<(ProgramID<N>, u16)>,
780 resource: Option<Identifier<N>>,
781 store: &dyn FinalizeStoreTrait<N>,
782 stack: &Stack<N>,
783 registers: &mut FinalizeRegisters<N>,
784 positions: &HashMap<Identifier<N>, usize>,
785 command: &Command<N>,
786 counter: &mut usize,
787 finalize_operations: &mut Vec<FinalizeOperation<N>>,
788 scope_name: &Identifier<N>,
789) -> Result<(), IndexedFinalizeError<N, Command<N>>> {
790 match &command {
792 Command::BranchEq(branch_eq) => {
793 let result = try_vm_runtime!(|| branch_to(*counter, branch_eq, positions, stack, registers));
794 match result {
795 Ok(Ok(new_counter)) => {
796 *counter = new_counter;
797 }
798 Ok(Err(error)) => indexed_finalize_bail!(
800 program_id,
801 resource,
802 *counter,
803 command.clone(),
804 "'{scope_name}' failed to evaluate command ({command}): {error}"
805 ),
806 Err(_) => indexed_finalize_bail!(
808 program_id,
809 resource,
810 *counter,
811 command.clone(),
812 "'{scope_name}' failed to evaluate command"
813 ),
814 }
815 }
816 Command::BranchNeq(branch_neq) => {
817 let result = try_vm_runtime!(|| branch_to(*counter, branch_neq, positions, stack, registers));
818 match result {
819 Ok(Ok(new_counter)) => {
820 *counter = new_counter;
821 }
822 Ok(Err(error)) => indexed_finalize_bail!(
824 program_id,
825 resource,
826 *counter,
827 command.clone(),
828 "'{scope_name}' failed to evaluate command: {error}"
829 ),
830 Err(_) => indexed_finalize_bail!(
832 program_id,
833 resource,
834 *counter,
835 command.clone(),
836 "'{scope_name}' failed to evaluate command"
837 ),
838 }
839 }
840 Command::Await(_) => {
841 indexed_finalize_bail!(
842 program_id,
843 resource,
844 *counter,
845 command.clone(),
846 "Cannot use `finalize_command_except_await` with an 'await' command"
847 )
848 }
849 _ => {
850 let result = try_vm_runtime!(|| command.finalize(stack, store, registers));
851 match result {
852 Ok(Ok(Some(finalize_operation))) => finalize_operations.push(finalize_operation),
854 Ok(Ok(None)) => {}
856 Ok(Err(error)) => {
858 return Err(IndexedFinalizeError::new(
859 program_id,
860 resource,
861 Some((*counter, command.clone())),
862 error,
863 ));
864 }
865 Err(_) => indexed_finalize_bail!(
867 program_id,
868 resource,
869 *counter,
870 command.clone(),
871 "'{scope_name}' failed to evaluate command"
872 ),
873 }
874 *counter += 1;
875 }
876 };
877 Ok(())
878}
879
880#[inline]
882fn setup_await<N: Network>(
883 state: FinalizeGlobalState,
884 await_: &Await<N>,
885 stack: &Arc<Stack<N>>,
886 registers: &FinalizeRegisters<N>,
887 transition_id: N::TransitionID,
888 nonce: u64,
889 dynamic_future_to_future: &HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>>,
890) -> Result<FinalizeState<N>> {
891 let (future, is_dynamic) = match registers.load(stack.deref(), &Operand::Register(await_.register().clone()))? {
893 Value::Future(future) => (future, false),
894 Value::DynamicFuture(dynamic_future) => {
895 let key = (
897 *dynamic_future.program_name(),
898 *dynamic_future.program_network(),
899 *dynamic_future.function_name(),
900 *dynamic_future.checksum(),
901 );
902 match dynamic_future_to_future.get(&key) {
904 Some(future) => ((*future).clone(), true),
905 None => bail!("Dynamic future '{key:?}' not found in dynamic-future-to-future map"),
906 }
907 }
908 _ => bail!("The input to 'await' is not a future or dynamic future"),
909 };
910 initialize_finalize_state(state, &future, stack, transition_id, nonce, is_dynamic)
912}
913
914fn branch_to<N: Network, const VARIANT: u8>(
916 counter: usize,
917 branch: &Branch<N, VARIANT>,
918 positions: &HashMap<Identifier<N>, usize>,
919 stack: &impl StackTrait<N>,
920 registers: &impl RegistersTrait<N>,
921) -> Result<usize> {
922 let first = registers.load(stack, branch.first())?;
924 let second = registers.load(stack, branch.second())?;
925
926 let get_position_index = |position: &Identifier<N>| match positions.get(position) {
928 Some(index) if *index > counter => Ok(*index),
929 Some(_) => bail!("Cannot branch to an earlier position '{position}' in the program"),
930 None => bail!("The position '{position}' does not exist."),
931 };
932
933 match VARIANT {
935 0 if first == second => get_position_index(branch.position()),
937 0 if first != second => Ok(counter + 1),
938 1 if first == second => Ok(counter + 1),
940 1 if first != second => get_position_index(branch.position()),
941 _ => bail!("Invalid 'branch' variant: {VARIANT}"),
942 }
943}
944
945#[cfg(test)]
946mod tests {
947 use super::*;
948 use crate::tests::test_execute::{sample_fee, sample_finalize_state};
949 use console::prelude::TestRng;
950 use snarkvm_ledger_store::{
951 BlockStore,
952 helpers::memory::{BlockMemory, FinalizeMemory},
953 };
954
955 use aleo_std::StorageMode;
956
957 type CurrentNetwork = console::network::MainnetV0;
958 type CurrentAleo = circuit::network::AleoV0;
959
960 #[test]
961 fn test_finalize_deployment() {
962 let rng = &mut TestRng::default();
963
964 let program = Program::<CurrentNetwork>::from_str(
966 r"
967program testing.aleo;
968
969struct message:
970 amount as u128;
971
972mapping account:
973 key as address.public;
974 value as u64.public;
975
976record token:
977 owner as address.private;
978 amount as u64.private;
979
980function initialize:
981 input r0 as address.private;
982 input r1 as u64.private;
983 cast r0 r1 into r2 as token.record;
984 output r2 as token.record;
985
986function compute:
987 input r0 as message.private;
988 input r1 as message.public;
989 input r2 as message.private;
990 input r3 as token.record;
991 add r0.amount r1.amount into r4;
992 cast r3.owner r3.amount into r5 as token.record;
993 output r4 as u128.public;
994 output r5 as token.record;",
995 )
996 .unwrap();
997
998 let process = Process::load().unwrap();
1000 let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
1002
1003 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1005 let finalize_store = FinalizeStore::<_, FinalizeMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1007
1008 assert!(!process.contains_program(program.id()));
1010
1011 let fee = sample_fee::<_, CurrentAleo, _, _>(&process, &block_store, &finalize_store, rng);
1013 let (stack, _) =
1015 process.lock().finalize_deployment(sample_finalize_state(1), &finalize_store, &deployment, &fee).unwrap();
1016 process.lock().add_stack(stack);
1018
1019 assert!(process.contains_program(program.id()));
1021 }
1022}