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