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 ®isters, 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 }