Skip to main content

snarkvm_synthesizer_process/
finalize.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 super::*;
17use console::program::{FinalizeType, Future, Register};
18use snarkvm_synthesizer_program::{Await, FinalizeRegistersState, Operand, RegistersTrait};
19use snarkvm_utilities::try_vm_runtime;
20
21use std::collections::HashSet;
22
23impl<N: Network> Process<N> {
24    /// Finalizes the deployment and fee.
25    /// This method assumes the given deployment **is valid**.
26    /// This method should **only** be called by `VM::finalize()`.
27    #[inline]
28    pub fn finalize_deployment<P: FinalizeStorage<N>>(
29        &self,
30        state: FinalizeGlobalState,
31        store: &FinalizeStore<N, P>,
32        deployment: &Deployment<N>,
33        fee: &Fee<N>,
34    ) -> Result<(Stack<N>, Vec<FinalizeOperation<N>>)> {
35        let timer = timer!("Process::finalize_deployment");
36
37        // Get the deployment version.
38        let version = deployment.version()?;
39
40        // Finalize the deployment based on its version.
41        match version {
42            DeploymentVersion::V1 | DeploymentVersion::V2 => {
43                // Compute the program stack.
44                let mut stack = Stack::new(self, deployment.program())?;
45                lap!(timer, "Compute the stack");
46
47                // Set the program owner.
48                stack.set_program_owner(deployment.program_owner());
49
50                // Insert all verifying keys (unified: functions + records).
51                for (name, (verifying_key, _)) in deployment.verifying_keys() {
52                    stack.insert_verifying_key(name, verifying_key.clone())?;
53                }
54                lap!(timer, "Insert the verifying keys");
55
56                // Determine which mappings must be initialized.
57                let mappings = match deployment.edition().is_zero() {
58                    true => deployment.program().mappings().values().collect::<Vec<_>>(),
59                    false => {
60                        // Get the existing stack.
61                        let existing_stack = self.get_stack(deployment.program_id())?;
62                        // Get the existing mappings.
63                        let existing_mappings = existing_stack.program().mappings();
64                        // Determine and return the new mappings
65                        let mut new_mappings = Vec::new();
66                        for mapping in deployment.program().mappings().values() {
67                            if !existing_mappings.contains_key(mapping.name()) {
68                                new_mappings.push(mapping);
69                            }
70                        }
71                        new_mappings
72                    }
73                };
74                lap!(timer, "Retrieve the mappings to initialize");
75
76                // Initialize the mappings, and store their finalize operations.
77                atomic_batch_scope!(store, {
78                    // Initialize a list for the finalize operations.
79                    let mut finalize_operations = Vec::with_capacity(deployment.program().mappings().len());
80
81                    /* Finalize the fee. */
82
83                    // Retrieve the fee stack.
84                    let fee_stack = self.get_stack(fee.program_id())?;
85                    // Finalize the fee transition.
86                    finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
87                    lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
88
89                    /* Finalize the deployment. */
90
91                    // Retrieve the program ID.
92                    let program_id = deployment.program_id();
93                    // Iterate over the mappings that must be initialized.
94                    for mapping in mappings {
95                        // Initialize the mapping.
96                        finalize_operations.push(store.initialize_mapping(*program_id, *mapping.name())?);
97                    }
98                    lap!(timer, "Initialize the program mappings");
99
100                    // If the program has a constructor, execute it and extend the finalize operations.
101                    // This must happen after the mappings are initialized as the constructor may depend on them.
102                    if deployment.program().contains_constructor() {
103                        let operations = finalize_constructor(state, store, &stack, N::TransitionID::default())?;
104                        finalize_operations.extend(operations);
105                        lap!(timer, "Execute the constructor");
106                    }
107
108                    finish!(timer, "Finished finalizing the deployment");
109                    // Return the stack and finalize operations.
110                    Ok((stack, finalize_operations))
111                })
112            }
113            DeploymentVersion::V3 => {
114                // Ensure that the program is not `credits.aleo`.
115                ensure!(
116                    deployment.program_id() != &ProgramID::credits(),
117                    "The 'credits.aleo' program cannot be deployed with DeploymentVersion::V3"
118                );
119
120                // Get the existing stack.
121                let existing_stack = self.get_stack(deployment.program_id())?;
122
123                // Compute a new stack with the same program and edition.
124                // Note: `Stack::new` cannot be used here because it would increment the edition.
125                // Amendments must preserve the existing edition. Validity is verified by `initialize_and_check`.
126                let mut stack = Stack::new_raw(self, deployment.program(), *existing_stack.program_edition())?;
127                stack.initialize_and_check(self)?;
128                lap!(timer, "Compute the stack");
129
130                // Set the program owner to the existing owner.
131                stack.set_program_owner(*existing_stack.program_owner());
132
133                // Insert all verifying keys (unified: functions + records).
134                for (name, (verifying_key, _)) in deployment.verifying_keys() {
135                    stack.insert_verifying_key(name, verifying_key.clone())?;
136                }
137                lap!(timer, "Insert the verifying keys");
138
139                // Finalize the fee (amendments don't initialize mappings or run constructors).
140                atomic_batch_scope!(store, {
141                    let mut finalize_operations = Vec::new();
142
143                    // Retrieve the fee stack.
144                    let fee_stack = self.get_stack(fee.program_id())?;
145                    // Finalize the fee transition.
146                    finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
147                    lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
148
149                    finish!(timer, "Finished finalizing the V3 deployment");
150                    Ok((stack, finalize_operations))
151                })
152            }
153        }
154    }
155
156    /// Finalizes the execution and fee.
157    /// This method assumes the given execution **is valid**.
158    /// This method should **only** be called by `VM::finalize()`.
159    #[inline]
160    pub fn finalize_execution<P: FinalizeStorage<N>>(
161        &self,
162        state: FinalizeGlobalState,
163        store: &FinalizeStore<N, P>,
164        execution: &Execution<N>,
165        fee: Option<&Fee<N>>,
166    ) -> Result<Vec<FinalizeOperation<N>>> {
167        let timer = timer!("Program::finalize_execution");
168
169        // Ensure the execution contains transitions.
170        ensure!(!execution.is_empty(), "There are no transitions in the execution");
171
172        // Ensure the number of transitions matches the program function.
173        // Retrieve the root transition (without popping it).
174        let transition = execution.peek()?;
175        // Retrieve the stack.
176        let stack = self.get_stack(transition.program_id())?;
177        // Calculate the minimum number of calls for the root transition.
178        let minimum_number_of_calls = stack.get_minimum_number_of_calls(transition.function_name())?;
179        // If the root transition contains a dynamic call,
180        // - ensure that the number of calls is less than or equal to the number of transitions.
181        // - otherwise, ensure that the number of calls matches the number of transitions.
182        if stack.contains_dynamic_call(transition.function_name())? {
183            ensure!(
184                minimum_number_of_calls <= execution.len(),
185                "The number of transitions in the execution is incorrect. Expected at least {minimum_number_of_calls}, but found {}",
186                execution.len()
187            );
188        } else {
189            ensure!(
190                minimum_number_of_calls == execution.len(),
191                "The number of transitions in the execution is incorrect. Expected {minimum_number_of_calls}, but found {}",
192                execution.len()
193            );
194        }
195        lap!(timer, "Verify the number of transitions");
196
197        // Collect all of the futures in the execution's transitions and compute their corresponding dynamic future keys.
198        // The key is (program_name, program_network, function_name, checksum). Futures with identical program,
199        // function, and arguments produce the same key and the same checksum by design — they represent the same
200        // logical future, so the map correctly de-duplicates them.
201        let dynamic_future_to_future: HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>> = execution
202            .transitions()
203            .filter_map(|transition| {
204                transition.outputs().last().and_then(|output| output.future()).and_then(|future| {
205                    let dynamic_future = DynamicFuture::from_future(future).ok()?;
206                    let key = (
207                        *dynamic_future.program_name(),
208                        *dynamic_future.program_network(),
209                        *dynamic_future.function_name(),
210                        *dynamic_future.checksum(),
211                    );
212                    Some((key, future))
213                })
214            })
215            .collect();
216
217        // Construct the call graph.
218        let consensus_version = N::CONSENSUS_VERSION(state.block_height())?;
219        let call_graph = match (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
220            true => self.construct_call_graph(execution.transitions())?,
221            // If the height is greater than or equal to `ConsensusVersion::V3`, then provide an empty call graph, as it is no longer used during finalization.
222            false => HashMap::new(),
223        };
224
225        atomic_batch_scope!(store, {
226            // Finalize the root transition.
227            // Note that this will result in all the remaining transitions being finalized, since the number
228            // of calls matches the number of transitions.
229            let mut finalize_operations =
230                finalize_transition(state, store, &stack, transition, call_graph, dynamic_future_to_future)?;
231
232            /* Finalize the fee. */
233            if let Some(fee) = fee {
234                // Retrieve the fee stack.
235                let fee_stack = self.get_stack(fee.program_id())?;
236                // Finalize the fee transition.
237                finalize_operations.extend(finalize_fee_transition(state, store, &fee_stack, fee)?);
238                lap!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
239            }
240
241            finish!(timer);
242            // Return the finalize operations.
243            Ok(finalize_operations)
244        })
245    }
246
247    /// Finalizes the fee.
248    /// This method assumes the given fee **is valid**.
249    /// This method should **only** be called by `VM::finalize()`.
250    #[inline]
251    pub fn finalize_fee<P: FinalizeStorage<N>>(
252        &self,
253        state: FinalizeGlobalState,
254        store: &FinalizeStore<N, P>,
255        fee: &Fee<N>,
256    ) -> Result<Vec<FinalizeOperation<N>>> {
257        let timer = timer!("Program::finalize_fee");
258
259        atomic_batch_scope!(store, {
260            // Retrieve the stack.
261            let stack = self.get_stack(fee.program_id())?;
262            // Finalize the fee transition.
263            let result = finalize_fee_transition(state, store, &stack, fee);
264            finish!(timer, "Finalize transition for '{}/{}'", fee.program_id(), fee.function_name());
265            // Return the result.
266            result
267        })
268    }
269}
270
271/// Finalizes the given fee transition.
272fn finalize_fee_transition<N: Network, P: FinalizeStorage<N>>(
273    state: FinalizeGlobalState,
274    store: &FinalizeStore<N, P>,
275    stack: &Arc<Stack<N>>,
276    fee: &Fee<N>,
277) -> Result<Vec<FinalizeOperation<N>>> {
278    // Construct the call graph.
279    let consensus_version = N::CONSENSUS_VERSION(state.block_height())?;
280    let call_graph = match (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
281        true => HashMap::from([(*fee.transition_id(), Vec::new())]),
282        // If the height is greater than or equal to `ConsensusVersion::V3`, then provide an empty call graph, as it is no longer used during finalization.
283        false => HashMap::new(),
284    };
285
286    // Finalize the transition.
287    match finalize_transition(state, store, stack, fee, call_graph, Default::default()) {
288        // If the evaluation succeeds, return the finalize operations.
289        Ok(finalize_operations) => Ok(finalize_operations),
290        // If the evaluation fails, bail and return the error.
291        Err(error) => bail!("'finalize' failed on '{}/{}' - {error}", fee.program_id(), fee.function_name()),
292    }
293}
294
295/// Finalizes the constructor.
296fn finalize_constructor<N: Network, P: FinalizeStorage<N>>(
297    state: FinalizeGlobalState,
298    store: &FinalizeStore<N, P>,
299    stack: &Stack<N>,
300    transition_id: N::TransitionID,
301) -> Result<Vec<FinalizeOperation<N>>> {
302    // Retrieve the program ID.
303    let program_id = stack.program_id();
304    dev_println!("Finalizing constructor for {}...", stack.program_id());
305
306    // Initialize a list for finalize operations.
307    let mut finalize_operations = Vec::new();
308
309    // Initialize a nonce for the constructor registers.
310    // Currently, this nonce is set to zero for every constructor.
311    let nonce = 0;
312
313    // Get the constructor logic. If the program does not have a constructor, return early.
314    let Some(constructor) = stack.program().constructor() else {
315        return Ok(finalize_operations);
316    };
317
318    // Get the constructor types.
319    let constructor_types = stack.get_constructor_types()?.clone();
320
321    // Initialize the finalize registers.
322    let mut registers = FinalizeRegisters::new(state, transition_id, *program_id.name(), constructor_types, nonce);
323
324    // Determine the scope name.
325    let scope_name = Identifier::<N>::from_str("constructor")?;
326
327    // Initialize a counter for the commands.
328    let mut counter = 0;
329
330    // Evaluate the commands.
331    while counter < constructor.commands().len() {
332        // Retrieve the command.
333        let command = &constructor.commands()[counter];
334        // Finalize the command.
335        match &command {
336            Command::Await(_) => {
337                bail!("Cannot `await` a Future in a constructor")
338            }
339            _ => finalize_command_except_await(
340                store,
341                stack,
342                &mut registers,
343                constructor.positions(),
344                command,
345                &mut counter,
346                &mut finalize_operations,
347                &scope_name,
348            )?,
349        };
350    }
351
352    // Return the finalize operations.
353    Ok(finalize_operations)
354}
355
356/// Finalizes the given transition.
357fn finalize_transition<N: Network, P: FinalizeStorage<N>>(
358    state: FinalizeGlobalState,
359    store: &FinalizeStore<N, P>,
360    stack: &Arc<Stack<N>>,
361    transition: &Transition<N>,
362    call_graph: HashMap<N::TransitionID, Vec<N::TransitionID>>,
363    dynamic_future_to_future: HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>>,
364) -> Result<Vec<FinalizeOperation<N>>> {
365    // Retrieve the program ID.
366    let program_id = transition.program_id();
367    // Retrieve the function name.
368    let function_name = transition.function_name();
369
370    dev_println!("Finalizing transition for {}/{function_name}...", transition.program_id());
371    debug_assert_eq!(stack.program_id(), transition.program_id());
372
373    // If the last output of the transition is a future, retrieve and finalize it. Otherwise, there are no operations to finalize.
374    let future = match transition.outputs().last().and_then(|output| output.future()) {
375        Some(future) => future,
376        _ => return Ok(Vec::new()),
377    };
378
379    // Check that the program ID and function name of the transition match those in the future.
380    ensure!(
381        future.program_id() == program_id && future.function_name() == function_name,
382        "The program ID and function name of the future do not match the transition"
383    );
384
385    // Initialize a list for finalize operations.
386    let mut finalize_operations = Vec::new();
387
388    // Initialize a stack of active finalize states.
389    let mut states = Vec::new();
390
391    // Initialize a nonce for the finalize registers.
392    // Note that this nonce must be unique for each sub-transition being finalized.
393    let mut nonce = 0;
394    // Top-level outputs are always static futures.
395    let is_dynamic = false;
396
397    // Initialize the top-level finalize state.
398    states.push(initialize_finalize_state(state, future, stack, *transition.id(), nonce, is_dynamic)?);
399
400    // While there are active finalize states, finalize them.
401    'outer: while let Some(FinalizeState { mut counter, mut registers, stack, mut call_counter, mut awaited }) =
402        states.pop()
403    {
404        // Get the finalize logic.
405        let Some(finalize) = stack.get_function_ref(registers.function_name())?.finalize_logic() else {
406            bail!(
407                "The function '{}/{}' does not have an associated finalize scope",
408                stack.program_id(),
409                registers.function_name()
410            )
411        };
412        // Determine the scope name.
413        let scope_name = *registers.function_name();
414        // Evaluate the commands.
415        while counter < finalize.commands().len() {
416            // Retrieve the command.
417            let command = &finalize.commands()[counter];
418            // Finalize the command.
419            match &command {
420                Command::Await(await_) => {
421                    // Check that the `await` register's is a locator.
422                    if let Register::Access(_, _) = await_.register() {
423                        bail!("The 'await' register must be a locator")
424                    };
425                    // Check that the future has not previously been awaited.
426                    ensure!(
427                        !awaited.contains(await_.register()),
428                        "The future register '{}' has already been awaited",
429                        await_.register()
430                    );
431
432                    // Get the transition ID used to initialize the finalize registers.
433                    // If the block height is greater than or equal to `ConsensusVersion::V3`, then use the top-level transition ID.
434                    // Otherwise, query the call graph for the child transition ID corresponding to the future that is being awaited.
435                    let consensus_version = N::CONSENSUS_VERSION(state.block_height())?;
436                    let transition_id = if (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
437                        // Get the current transition ID.
438                        let transition_id = registers.transition_id();
439                        // Get the child transition ID.
440                        match call_graph.get(transition_id) {
441                            Some(transitions) => match transitions.get(call_counter) {
442                                Some(transition_id) => *transition_id,
443                                None => bail!("Child transition ID not found."),
444                            },
445                            None => bail!("Transition ID '{transition_id}' not found in call graph"),
446                        }
447                    } else {
448                        *transition.id()
449                    };
450
451                    // Increment the nonce.
452                    nonce += 1;
453
454                    // Set up the finalize state for the await.
455                    let callee_state = match try_vm_runtime!(|| setup_await(
456                        state,
457                        await_,
458                        &stack,
459                        &registers,
460                        transition_id,
461                        nonce,
462                        &dynamic_future_to_future,
463                    )) {
464                        Ok(Ok(callee_state)) => callee_state,
465                        // If the evaluation fails, bail and return the error.
466                        Ok(Err(error)) => bail!("'finalize' failed to evaluate command ({command}): {error}"),
467                        // If the evaluation fails, bail and return the error.
468                        Err(_) => bail!("'finalize' failed to evaluate command ({command})"),
469                    };
470
471                    // Increment the call counter.
472                    call_counter += 1;
473                    // Increment the counter.
474                    counter += 1;
475                    // Add the awaited register to the tracked set.
476                    awaited.insert(await_.register().clone());
477
478                    // Aggregate the caller state.
479                    let caller_state = FinalizeState { counter, registers, stack, call_counter, awaited };
480
481                    // Push the caller state onto the stack.
482                    states.push(caller_state);
483                    // Push the callee state onto the stack.
484                    states.push(callee_state);
485
486                    continue 'outer;
487                }
488                _ => finalize_command_except_await(
489                    store,
490                    stack.deref(),
491                    &mut registers,
492                    finalize.positions(),
493                    command,
494                    &mut counter,
495                    &mut finalize_operations,
496                    &scope_name,
497                )?,
498            };
499        }
500        // Check that all future registers have been awaited.
501        let mut unawaited = Vec::new();
502        for input in finalize.inputs() {
503            if matches!(input.finalize_type(), FinalizeType::Future(_) | FinalizeType::DynamicFuture)
504                && !awaited.contains(input.register())
505            {
506                unawaited.push(input.register().clone());
507            }
508        }
509        ensure!(
510            unawaited.is_empty(),
511            "The following future registers have not been awaited: {}",
512            unawaited.iter().map(|r| r.to_string()).collect::<Vec<_>>().join(", ")
513        );
514    }
515
516    // Return the finalize operations.
517    Ok(finalize_operations)
518}
519
520// A helper struct to track the execution of a finalize scope.
521struct FinalizeState<N: Network> {
522    // A counter for the index of the commands.
523    counter: usize,
524    // The registers.
525    registers: FinalizeRegisters<N>,
526    // The stack.
527    stack: Arc<Stack<N>>,
528    // Call counter.
529    call_counter: usize,
530    // Awaited futures.
531    awaited: HashSet<Register<N>>,
532}
533
534// A helper function to initialize the finalize state for transitions (not constructors).
535fn initialize_finalize_state<N: Network>(
536    state: FinalizeGlobalState,
537    future: &Future<N>,
538    stack: &Arc<Stack<N>>,
539    transition_id: N::TransitionID,
540    nonce: u64,
541    is_dynamic: bool,
542) -> Result<FinalizeState<N>> {
543    // Get the stack.
544    let stack = match (stack.program_id() == future.program_id(), is_dynamic) {
545        (true, _) => stack.clone(),
546        (false, true) => stack.get_stack_global(future.program_id())?,
547        (false, false) => stack.get_external_stack(future.program_id())?,
548    };
549    // Get the finalize logic and check that it exists.
550    let Some(finalize) = stack.get_function_ref(future.function_name())?.finalize_logic() else {
551        bail!(
552            "The function '{}/{}' does not have an associated finalize scope",
553            future.program_id(),
554            future.function_name()
555        )
556    };
557    // Initialize the registers.
558    let mut registers = FinalizeRegisters::new(
559        state,
560        transition_id,
561        *future.function_name(),
562        stack.get_finalize_types(future.function_name())?.clone(),
563        nonce,
564    );
565
566    // Store the inputs. The argument count is guaranteed to match the finalize's declared inputs
567    // because the Future was validated against the finalize type signature at execution time.
568    finalize.inputs().iter().map(|i| i.register()).zip_eq(future.arguments().iter()).try_for_each(
569        |(register, input)| {
570            // Assign the input value to the register.
571            registers.store(stack.deref(), register, Value::from(input))
572        },
573    )?;
574
575    Ok(FinalizeState { counter: 0, registers, stack, call_counter: 0, awaited: Default::default() })
576}
577
578// A helper function to finalize all commands except `await`, updating the finalize operations and the counter.
579#[inline]
580fn finalize_command_except_await<N: Network>(
581    store: &FinalizeStore<N, impl FinalizeStorage<N>>,
582    stack: &impl StackTrait<N>,
583    registers: &mut FinalizeRegisters<N>,
584    positions: &HashMap<Identifier<N>, usize>,
585    command: &Command<N>,
586    counter: &mut usize,
587    finalize_operations: &mut Vec<FinalizeOperation<N>>,
588    scope_name: &Identifier<N>,
589) -> Result<()> {
590    // Finalize the command.
591    match &command {
592        Command::BranchEq(branch_eq) => {
593            let result = try_vm_runtime!(|| branch_to(*counter, branch_eq, positions, stack, registers));
594            match result {
595                Ok(Ok(new_counter)) => {
596                    *counter = new_counter;
597                }
598                // If the evaluation fails, bail and return the error.
599                Ok(Err(error)) => bail!("'{scope_name}' failed to evaluate command ({command}): {error}"),
600                // If the evaluation fails, bail and return the error.
601                Err(_) => bail!("'{scope_name}' failed to evaluate command ({command})"),
602            }
603        }
604        Command::BranchNeq(branch_neq) => {
605            let result = try_vm_runtime!(|| branch_to(*counter, branch_neq, positions, stack, registers));
606            match result {
607                Ok(Ok(new_counter)) => {
608                    *counter = new_counter;
609                }
610                // If the evaluation fails, bail and return the error.
611                Ok(Err(error)) => bail!("'{scope_name}' failed to evaluate command ({command}): {error}"),
612                // If the evaluation fails, bail and return the error.
613                Err(_) => bail!("'{scope_name}' failed to evaluate command ({command})"),
614            }
615        }
616        Command::Await(_) => {
617            bail!("Cannot use `finalize_command_except_await` with an 'await' command")
618        }
619        _ => {
620            let result = try_vm_runtime!(|| command.finalize(stack, store, registers));
621            match result {
622                // If the evaluation succeeds with an operation, add it to the list.
623                Ok(Ok(Some(finalize_operation))) => finalize_operations.push(finalize_operation),
624                // If the evaluation succeeds with no operation, continue.
625                Ok(Ok(None)) => {}
626                // If the evaluation fails, bail and return the error.
627                Ok(Err(error)) => bail!("'{scope_name}' failed to evaluate command ({command}): {error}"),
628                // If the evaluation fails, bail and return the error.
629                Err(_) => bail!("'{scope_name}' failed to evaluate command ({command})"),
630            }
631            *counter += 1;
632        }
633    };
634    Ok(())
635}
636
637// A helper function that sets up the await operation.
638#[inline]
639fn setup_await<N: Network>(
640    state: FinalizeGlobalState,
641    await_: &Await<N>,
642    stack: &Arc<Stack<N>>,
643    registers: &FinalizeRegisters<N>,
644    transition_id: N::TransitionID,
645    nonce: u64,
646    dynamic_future_to_future: &HashMap<(Field<N>, Field<N>, Field<N>, Field<N>), &Future<N>>,
647) -> Result<FinalizeState<N>> {
648    // Retrieve the input as a future.
649    let (future, is_dynamic) = match registers.load(stack.deref(), &Operand::Register(await_.register().clone()))? {
650        Value::Future(future) => (future, false),
651        Value::DynamicFuture(dynamic_future) => {
652            // Construct the key from the dynamic future's program name, network, function name, and checksum.
653            let key = (
654                *dynamic_future.program_name(),
655                *dynamic_future.program_network(),
656                *dynamic_future.function_name(),
657                *dynamic_future.checksum(),
658            );
659            // Look up the corresponding future from the dynamic future key.
660            match dynamic_future_to_future.get(&key) {
661                Some(future) => ((*future).clone(), true),
662                None => bail!("Dynamic future '{key:?}' not found in dynamic-future-to-future map"),
663            }
664        }
665        _ => bail!("The input to 'await' is not a future or dynamic future"),
666    };
667    // Initialize the state.
668    initialize_finalize_state(state, &future, stack, transition_id, nonce, is_dynamic)
669}
670
671// A helper function that returns the index to branch to.
672fn branch_to<N: Network, const VARIANT: u8>(
673    counter: usize,
674    branch: &Branch<N, VARIANT>,
675    positions: &HashMap<Identifier<N>, usize>,
676    stack: &impl StackTrait<N>,
677    registers: &impl RegistersTrait<N>,
678) -> Result<usize> {
679    // Retrieve the inputs.
680    let first = registers.load(stack, branch.first())?;
681    let second = registers.load(stack, branch.second())?;
682
683    // A helper to get the index corresponding to a position.
684    let get_position_index = |position: &Identifier<N>| match positions.get(position) {
685        Some(index) if *index > counter => Ok(*index),
686        Some(_) => bail!("Cannot branch to an earlier position '{position}' in the program"),
687        None => bail!("The position '{position}' does not exist."),
688    };
689
690    // Compare the operands and determine the index to branch to.
691    match VARIANT {
692        // The `branch.eq` variant.
693        0 if first == second => get_position_index(branch.position()),
694        0 if first != second => Ok(counter + 1),
695        // The `branch.neq` variant.
696        1 if first == second => Ok(counter + 1),
697        1 if first != second => get_position_index(branch.position()),
698        _ => bail!("Invalid 'branch' variant: {VARIANT}"),
699    }
700}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705    use crate::tests::test_execute::{sample_fee, sample_finalize_state};
706    use console::prelude::TestRng;
707    use snarkvm_ledger_store::{
708        BlockStore,
709        helpers::memory::{BlockMemory, FinalizeMemory},
710    };
711
712    use aleo_std::StorageMode;
713
714    type CurrentNetwork = console::network::MainnetV0;
715    type CurrentAleo = circuit::network::AleoV0;
716
717    #[test]
718    fn test_finalize_deployment() {
719        let rng = &mut TestRng::default();
720
721        // Initialize a new program.
722        let program = Program::<CurrentNetwork>::from_str(
723            r"
724program testing.aleo;
725
726struct message:
727    amount as u128;
728
729mapping account:
730    key as address.public;
731    value as u64.public;
732
733record token:
734    owner as address.private;
735    amount as u64.private;
736
737function initialize:
738    input r0 as address.private;
739    input r1 as u64.private;
740    cast r0 r1 into r2 as token.record;
741    output r2 as token.record;
742
743function compute:
744    input r0 as message.private;
745    input r1 as message.public;
746    input r2 as message.private;
747    input r3 as token.record;
748    add r0.amount r1.amount into r4;
749    cast r3.owner r3.amount into r5 as token.record;
750    output r4 as u128.public;
751    output r5 as token.record;",
752        )
753        .unwrap();
754
755        // Initialize a new process.
756        let mut process = Process::load().unwrap();
757        // Deploy the program.
758        let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
759
760        // Initialize a new block store.
761        let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
762        // Initialize a new finalize store.
763        let finalize_store = FinalizeStore::<_, FinalizeMemory<_>>::open(StorageMode::new_test(None)).unwrap();
764
765        // Ensure the program does not exist.
766        assert!(!process.contains_program(program.id()));
767
768        // Compute the fee.
769        let fee = sample_fee::<_, CurrentAleo, _, _>(&process, &block_store, &finalize_store, rng);
770        // Finalize the deployment.
771        let (stack, _) =
772            process.finalize_deployment(sample_finalize_state(1), &finalize_store, &deployment, &fee).unwrap();
773        // Add the stack *manually* to the process.
774        process.add_stack(stack);
775
776        // Ensure the program exists.
777        assert!(process.contains_program(program.id()));
778    }
779}