/ synthesizer / process / src / cost.rs
cost.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 std::collections::HashMap;
  17  
  18  use crate::{Authorization, FinalizeTypes, Process, Stack, StackRef, StackTrait};
  19  
  20  use alphavm_algorithms::snark::varuna::VarunaVersion;
  21  use alphavm_ledger_block::{Deployment, Execution, Transaction};
  22  use alphavm_synthesizer_program::{CastType, Command, Instruction, Operand};
  23  use alphavm_synthesizer_snark::proof_size;
  24  use console::{
  25      prelude::*,
  26      program::{FinalizeType, Identifier, LiteralType, PlaintextType},
  27  };
  28  
  29  pub type MinimumCost = u64;
  30  pub type StorageCost = u64;
  31  pub type SynthesisCost = u64;
  32  pub type ConstructorCost = u64;
  33  pub type NamespaceCost = u64;
  34  pub type FinalizeCost = u64;
  35  pub type DeployCostDetails = (StorageCost, SynthesisCost, ConstructorCost, NamespaceCost);
  36  pub type ExecuteCostDetails = (StorageCost, FinalizeCost);
  37  
  38  /// Returns the deployment cost in microcredits for a given deployment.
  39  pub fn deployment_cost<N: Network>(
  40      process: &Process<N>,
  41      deployment: &Deployment<N>,
  42      consensus_version: ConsensusVersion,
  43  ) -> Result<(MinimumCost, DeployCostDetails)> {
  44      if consensus_version >= ConsensusVersion::V10 {
  45          deployment_cost_v2(process, deployment)
  46      } else {
  47          deployment_cost_v1(process, deployment)
  48      }
  49  }
  50  
  51  /// Returns the execution cost in microcredits for a given execution.
  52  pub fn execution_cost<N: Network>(
  53      process: &Process<N>,
  54      execution: &Execution<N>,
  55      consensus_version: ConsensusVersion,
  56  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
  57      let execution_size = execution.size_in_bytes()?;
  58  
  59      execution_cost_given_size(process, execution, execution_size, consensus_version)
  60  }
  61  
  62  // Returns the execution cost in microcredits for a given execution whose size is provided as an argument.
  63  fn execution_cost_given_size<N: Network>(
  64      process: &Process<N>,
  65      execution: &Execution<N>,
  66      execution_size: u64,
  67      consensus_version: ConsensusVersion,
  68  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
  69      if consensus_version >= ConsensusVersion::V10 {
  70          execution_cost_v3(process, execution, execution_size)
  71      } else if consensus_version >= ConsensusVersion::V2 {
  72          execution_cost_v2(process, execution, execution_size)
  73      } else {
  74          execution_cost_v1(process, execution, execution_size)
  75      }
  76  }
  77  
  78  /// Returns the execution cost in microcredits for a given `Authorization.
  79  pub fn execution_cost_for_authorization<N: Network>(
  80      process: &Process<N>,
  81      authorization: &Authorization<N>,
  82      consensus_version: ConsensusVersion,
  83  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
  84      ensure!(
  85          consensus_version >= ConsensusVersion::V4,
  86          "Execution-cost computation for authorization relies on proof-size estimation, which is only implemented for Varuna version >= V2 (consensus version >= V4)"
  87      );
  88  
  89      // Reconstruct an Execution from the Authorization. Note that the StateRoot
  90      // does not affect the fee (it has constant size).
  91      let reconstructed_execution =
  92          Execution::from(authorization.transitions().values().cloned(), N::StateRoot::default(), None)?;
  93  
  94      // Compute the size of the proof that will result from proving the
  95      // Authorization. The first step is to compute the Varuna batch sizes. The
  96      // Varuna circuits that must be proved as part of an Execution are:
  97      // - the circuits of each Transition
  98      // - one inclusion circuit for input records to *all* of those Transitions
  99  
 100      // TODO: Dynamic dispatch, once implemented, will cause a third type of
 101      // circuit to appear which needs to be accounted for here.
 102  
 103      let mut circuit_frequencies = HashMap::new();
 104  
 105      // In order to compute the frequencies of function circuits, we mimic the
 106      // operation of Process::verify_execution:
 107      for transition in authorization.transitions().values() {
 108          let entry =
 109              circuit_frequencies.entry((*transition.program_id(), *transition.function_name())).or_insert(0usize);
 110          *entry += 1;
 111      }
 112  
 113      let mut batch_sizes: Vec<usize> = circuit_frequencies.values().cloned().collect();
 114  
 115      // We now add the single batch of inclusion circuits for input records, if
 116      // any:
 117      let n_input_records = Authorization::number_of_input_records(authorization.transitions().values());
 118      if n_input_records > 0 {
 119          batch_sizes.push(n_input_records);
 120      }
 121  
 122      // Varuna is always run in hiding (i. e. ZK) mode when proving Executions.
 123      let hiding_mode = true;
 124  
 125      // If future versions of Varuna are introduced, the correct one should be
 126      // deduced here from the consensus version. Currently only the latest Varuna
 127      // version V2 is supported.
 128      let varuna_version = VarunaVersion::V2;
 129  
 130      let expected_proof_size = u64::try_from(proof_size::<N>(&batch_sizes, varuna_version, hiding_mode)?)?;
 131      let unproved_execution_size = reconstructed_execution.size_in_bytes()?;
 132      let execution_size = unproved_execution_size.checked_add(expected_proof_size).ok_or(anyhow!(
 133          "The execution size computation overflowed for an authorization when the proof was taken into account"
 134      ))?;
 135  
 136      execution_cost_given_size(process, &reconstructed_execution, execution_size, consensus_version)
 137  }
 138  
 139  /// Returns the compute cost for a deployment in microcredits.
 140  /// This is used to limit the amount of single-threaded compute in the block generation hot
 141  /// path. This does NOT represent the full costs which a user has to pay.
 142  pub fn deploy_compute_cost_in_microcredits(
 143      cost_details: DeployCostDetails,
 144      consensus_version: ConsensusVersion,
 145  ) -> Result<u64> {
 146      let (storage_cost, synthesis_cost, constructor_cost, _) = cost_details;
 147      let cost_to_check = if consensus_version >= ConsensusVersion::V10 {
 148          // From V10, only include the constructor compute cost for
 149          // deployments.
 150          //
 151          // The limits of individual function's finalize compute costs are
 152          // checked in calls to `deployment_cost`.
 153          constructor_cost
 154      } else {
 155          // Include the storage, synthesis, and constructor cost for deployments.
 156          storage_cost
 157              .checked_add(synthesis_cost)
 158              .and_then(|synthesis_cost| synthesis_cost.checked_add(constructor_cost))
 159              .ok_or(anyhow!("The storage, synthesis, and constructor cost computation overflowed for a deployment"))?
 160      };
 161      Ok(cost_to_check)
 162  }
 163  
 164  /// Returns the compute cost for an execution in microcredits.
 165  /// This is used to limit the amount of single-threaded compute in the block generation hot
 166  /// path. This does NOT represent the full costs which a user has to pay.
 167  pub fn execute_compute_cost_in_microcredits(
 168      cost_details: ExecuteCostDetails,
 169      consensus_version: ConsensusVersion,
 170  ) -> Result<u64> {
 171      let (storage_cost, finalize_cost) = cost_details;
 172      let cost_to_check = if consensus_version >= ConsensusVersion::V10 {
 173          // From V10, only include the finalize compute cost for executions.
 174          finalize_cost
 175      } else {
 176          // Include the finalize cost and storage cost for executions.
 177          storage_cost
 178              .checked_add(finalize_cost)
 179              .ok_or(anyhow!("The storage and finalize cost computation overflowed for an execution"))?
 180      };
 181      Ok(cost_to_check)
 182  }
 183  
 184  /// Returns the *minimum* cost in microcredits to publish the given deployment using the ARC_0005_COMPUTE_DISCOUNT.
 185  pub fn deployment_cost_v2<N: Network>(
 186      process: &Process<N>,
 187      deployment: &Deployment<N>,
 188  ) -> Result<(MinimumCost, DeployCostDetails)> {
 189      // Determine the number of bytes in the deployment.
 190      let size_in_bytes = deployment.size_in_bytes()?;
 191      // Retrieve the program ID.
 192      let program_id = deployment.program_id();
 193      // Determine the number of characters in the program ID.
 194      let num_characters = u32::try_from(program_id.name().to_string().len())?;
 195      // Compute the number of combined variables in the program.
 196      let num_combined_variables = deployment.num_combined_variables()?;
 197      // Compute the number of combined constraints in the program.
 198      let num_combined_constraints = deployment.num_combined_constraints()?;
 199  
 200      // Compute the storage cost in microcredits.
 201      let storage_cost = size_in_bytes
 202          .checked_mul(N::DEPLOYMENT_FEE_MULTIPLIER)
 203          .ok_or(anyhow!("The storage cost computation overflowed for a deployment"))?;
 204  
 205      // Compute the synthesis cost in microcredits.
 206      let synthesis_cost = num_combined_variables.saturating_add(num_combined_constraints) * N::SYNTHESIS_FEE_MULTIPLIER
 207          / N::ARC_0005_COMPUTE_DISCOUNT;
 208  
 209      // Compute a Stack for the deployment.
 210      let stack = Stack::new(process, deployment.program())?;
 211  
 212      // Compute the constructor cost in microcredits.
 213      let constructor_cost = constructor_cost_in_microcredits_v2(&stack)?;
 214  
 215      // Check that the functions are valid.
 216      for function in deployment.program().functions().values() {
 217          // Get the finalize cost.
 218          let finalize_cost = cost_in_microcredits_v3(&stack, function.name())?;
 219          // Check that the finalize cost does not exceed the maximum.
 220          ensure!(
 221              finalize_cost <= N::TRANSACTION_SPEND_LIMIT[1].1,
 222              "Finalize block '{}' has a cost '{finalize_cost}' which exceeds the transaction spend limit '{}'",
 223              function.name(),
 224              N::TRANSACTION_SPEND_LIMIT[1].1
 225          );
 226      }
 227  
 228      // Compute the namespace cost in microcredits: 10^(10 - num_characters) * 1e6
 229      let namespace_cost = 10u64
 230          .checked_pow(10u32.saturating_sub(num_characters))
 231          .ok_or(anyhow!("The namespace cost computation overflowed for a deployment"))?
 232          .saturating_mul(1_000_000); // 1 microcredit = 1e-6 credits.
 233  
 234      // Compute the minimum cost in microcredits.
 235      let minimum_cost = storage_cost
 236          .checked_add(synthesis_cost)
 237          .and_then(|x| x.checked_add(constructor_cost))
 238          .and_then(|x| x.checked_add(namespace_cost))
 239          .ok_or(anyhow!("The total cost computation overflowed for a deployment"))?;
 240  
 241      Ok((minimum_cost, (storage_cost, synthesis_cost, constructor_cost, namespace_cost)))
 242  }
 243  
 244  /// Returns the *minimum* cost in microcredits to publish the given deployment.
 245  pub fn deployment_cost_v1<N: Network>(
 246      process: &Process<N>,
 247      deployment: &Deployment<N>,
 248  ) -> Result<(MinimumCost, DeployCostDetails)> {
 249      // Determine the number of bytes in the deployment.
 250      let size_in_bytes = deployment.size_in_bytes()?;
 251      // Retrieve the program ID.
 252      let program_id = deployment.program_id();
 253      // Determine the number of characters in the program ID.
 254      let num_characters = u32::try_from(program_id.name().to_string().len())?;
 255      // Compute the number of combined variables in the program.
 256      let num_combined_variables = deployment.num_combined_variables()?;
 257      // Compute the number of combined constraints in the program.
 258      let num_combined_constraints = deployment.num_combined_constraints()?;
 259  
 260      // Compute the storage cost in microcredits.
 261      let storage_cost = size_in_bytes
 262          .checked_mul(N::DEPLOYMENT_FEE_MULTIPLIER)
 263          .ok_or(anyhow!("The storage cost computation overflowed for a deployment"))?;
 264  
 265      // Compute the synthesis cost in microcredits.
 266      let synthesis_cost = num_combined_variables.saturating_add(num_combined_constraints) * N::SYNTHESIS_FEE_MULTIPLIER;
 267  
 268      // Compute a Stack for the deployment.
 269      let stack = Stack::new(process, deployment.program())?;
 270  
 271      // Compute the constructor cost in microcredits.
 272      let constructor_cost = constructor_cost_in_microcredits_v1(&stack)?;
 273  
 274      // Check that the functions are valid.
 275      for function in deployment.program().functions().values() {
 276          // Get the finalize cost.
 277          let finalize_cost = cost_in_microcredits_v2(&stack, function.name())?;
 278          // Check that the finalize cost does not exceed the maximum.
 279          ensure!(
 280              finalize_cost <= N::TRANSACTION_SPEND_LIMIT[0].1,
 281              "Finalize block '{}' has a cost '{finalize_cost}' which exceeds the transaction spend limit '{}'",
 282              function.name(),
 283              N::TRANSACTION_SPEND_LIMIT[0].1
 284          );
 285      }
 286  
 287      // Compute the namespace cost in microcredits: 10^(10 - num_characters) * 1e6
 288      let namespace_cost = 10u64
 289          .checked_pow(10u32.saturating_sub(num_characters))
 290          .ok_or(anyhow!("The namespace cost computation overflowed for a deployment"))?
 291          .saturating_mul(1_000_000); // 1 microcredit = 1e-6 credits.
 292  
 293      // Compute the minimum cost in microcredits.
 294      let minimum_cost = storage_cost
 295          .checked_add(synthesis_cost)
 296          .and_then(|x| x.checked_add(constructor_cost))
 297          .and_then(|x| x.checked_add(namespace_cost))
 298          .ok_or(anyhow!("The total cost computation overflowed for a deployment"))?;
 299  
 300      Ok((minimum_cost, (storage_cost, synthesis_cost, constructor_cost, namespace_cost)))
 301  }
 302  
 303  /// Returns the *minimum* cost in microcredits to publish the given execution using the ARC_0005_COMPUTE_DISCOUNT.
 304  fn execution_cost_v3<N: Network>(
 305      process: &Process<N>,
 306      execution: &Execution<N>,
 307      execution_size: u64,
 308  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
 309      // Compute the storage cost in microcredits.
 310      let storage_cost = execution_storage_cost::<N>(execution_size);
 311  
 312      // Get the root transition.
 313      let transition = execution.peek()?;
 314  
 315      // Get the finalize cost for the root transition.
 316      let stack = process.get_stack(transition.program_id())?;
 317      let finalize_cost = cost_in_microcredits_v3(&stack, transition.function_name())?;
 318  
 319      // Compute the minimum cost in microcredits.
 320      let minimum_cost = storage_cost
 321          .checked_add(finalize_cost)
 322          .ok_or(anyhow!("The total cost computation overflowed for an execution"))?;
 323  
 324      Ok((minimum_cost, (storage_cost, finalize_cost)))
 325  }
 326  
 327  /// Returns the *minimum* cost in microcredits to publish the given execution.
 328  fn execution_cost_v2<N: Network>(
 329      process: &Process<N>,
 330      execution: &Execution<N>,
 331      execution_size: u64,
 332  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
 333      // Compute the storage cost in microcredits.
 334      let storage_cost = execution_storage_cost::<N>(execution_size);
 335  
 336      // Get the root transition.
 337      let transition = execution.peek()?;
 338  
 339      // Get the finalize cost for the root transition.
 340      let stack = process.get_stack(transition.program_id())?;
 341      let finalize_cost = cost_in_microcredits_v2(&stack, transition.function_name())?;
 342  
 343      // Compute the total cost in microcredits.
 344      let total_cost = storage_cost
 345          .checked_add(finalize_cost)
 346          .ok_or(anyhow!("The total cost computation overflowed for an execution"))?;
 347  
 348      Ok((total_cost, (storage_cost, finalize_cost)))
 349  }
 350  
 351  /// Returns the *minimum* cost in microcredits to publish the given execution.
 352  fn execution_cost_v1<N: Network>(
 353      process: &Process<N>,
 354      execution: &Execution<N>,
 355      execution_size: u64,
 356  ) -> Result<(MinimumCost, ExecuteCostDetails)> {
 357      // Compute the storage cost in microcredits.
 358      let storage_cost = execution_storage_cost::<N>(execution_size);
 359  
 360      // Get the root transition.
 361      let transition = execution.peek()?;
 362  
 363      // Get the finalize cost for the root transition.
 364      let stack = process.get_stack(transition.program_id())?;
 365      let finalize_cost = cost_in_microcredits_v1(&stack, transition.function_name())?;
 366  
 367      // Compute the total cost in microcredits.
 368      let total_cost = storage_cost
 369          .checked_add(finalize_cost)
 370          .ok_or(anyhow!("The total cost computation overflowed for an execution"))?;
 371  
 372      Ok((total_cost, (storage_cost, finalize_cost)))
 373  }
 374  
 375  /// Returns the storage cost in microcredits for a program execution.
 376  fn execution_storage_cost<N: Network>(size_in_bytes: u64) -> u64 {
 377      if size_in_bytes > N::EXECUTION_STORAGE_PENALTY_THRESHOLD {
 378          size_in_bytes.saturating_mul(size_in_bytes).saturating_div(N::EXECUTION_STORAGE_FEE_SCALING_FACTOR)
 379      } else {
 380          size_in_bytes
 381      }
 382  }
 383  
 384  // Finalize costs for compute heavy operations, derived as:
 385  // `BASE_COST + (PER_BYTE_COST * SIZE_IN_BYTES)`.
 386  
 387  const CAST_BASE_COST: u64 = 500;
 388  const CAST_PER_BYTE_COST: u64 = 30;
 389  
 390  const HASH_BASE_COST: u64 = 10_000;
 391  const HASH_PER_BYTE_COST: u64 = 30;
 392  
 393  const HASH_BHP_BASE_COST: u64 = 50_000;
 394  const HASH_BHP_PER_BYTE_COST: u64 = 300;
 395  
 396  const HASH_PSD_BASE_COST: u64 = 40_000;
 397  const HASH_PSD_PER_BYTE_COST: u64 = 75;
 398  
 399  const ECDSA_VERIFY_BASE_COST: u64 = 60_000;
 400  const ECDSA_VERIFY_ETH_BASE_COST: u64 = 75_000;
 401  
 402  #[derive(Copy, Clone)]
 403  pub enum ConsensusFeeVersion {
 404      V1,
 405      V2,
 406      V3,
 407  }
 408  
 409  const MAPPING_BASE_COST_V1: u64 = 10_000;
 410  const MAPPING_BASE_COST_V2: u64 = 1_500;
 411  const MAPPING_PER_BYTE_COST: u64 = 10;
 412  
 413  const SET_BASE_COST: u64 = 10_000;
 414  const SET_PER_BYTE_COST: u64 = 100;
 415  
 416  /// A helper function to determine the plaintext type in bytes.
 417  fn plaintext_size_in_bytes<N: Network>(stack: &Stack<N>, plaintext_type: &PlaintextType<N>) -> Result<u64> {
 418      match plaintext_type {
 419          PlaintextType::Literal(literal_type) => Ok(literal_type.size_in_bytes::<N>() as u64),
 420          PlaintextType::Struct(struct_name) => {
 421              // Retrieve the struct from the stack.
 422              let struct_ = stack.program().get_struct(struct_name)?;
 423              // Retrieve the size of the struct name.
 424              let size_of_name = struct_.name().to_bytes_le()?.len() as u64;
 425              // Retrieve the size of all the members of the struct.
 426              let size_of_members = struct_.members().iter().try_fold(0u64, |acc, (_, member_type)| {
 427                  acc.checked_add(plaintext_size_in_bytes(stack, member_type)?).ok_or(anyhow!(
 428                      "Overflowed while computing the size of the struct '{}/{struct_name}' - {member_type}",
 429                      stack.program_id()
 430                  ))
 431              })?;
 432              // Return the size of the struct.
 433              Ok(size_of_name.saturating_add(size_of_members))
 434          }
 435          PlaintextType::Array(array_type) => {
 436              // Retrieve the number of elements in the array.
 437              let num_elements = **array_type.length() as u64;
 438              // Compute the size of an array element.
 439              let size_of_element = plaintext_size_in_bytes(stack, array_type.next_element_type())?;
 440              // Return the size of the array.
 441              Ok(num_elements.saturating_mul(size_of_element))
 442          }
 443      }
 444  }
 445  
 446  /// A helper function to compute the following: base_cost + (byte_multiplier * size_of_operands).
 447  fn cost_in_size<'a, N: Network>(
 448      stack: &Stack<N>,
 449      finalize_types: &FinalizeTypes<N>,
 450      operands: impl IntoIterator<Item = &'a Operand<N>>,
 451      byte_multiplier: u64,
 452      base_cost: u64,
 453  ) -> Result<u64> {
 454      // Compute the size of the operands.
 455      let size_of_operands = operands.into_iter().try_fold(0u64, |acc, operand| {
 456          // Determine the size of the operand.
 457          let operand_size = match finalize_types.get_type_from_operand(stack, operand)? {
 458              FinalizeType::Plaintext(plaintext_type) => plaintext_size_in_bytes(stack, &plaintext_type)?,
 459              FinalizeType::Future(future) => {
 460                  bail!("Future '{future}' is not a valid operand");
 461              }
 462          };
 463          // Safely add the size to the accumulator.
 464          acc.checked_add(operand_size).ok_or(anyhow!(
 465              "Overflowed while computing the size of the operand '{operand}' in '{}'",
 466              stack.program_id(),
 467          ))
 468      })?;
 469      // Return the cost.
 470      Ok(base_cost.saturating_add(byte_multiplier.saturating_mul(size_of_operands)))
 471  }
 472  
 473  /// Returns the the cost of a command in a finalize scope.
 474  pub fn cost_per_command<N: Network>(
 475      stack: &Stack<N>,
 476      finalize_types: &FinalizeTypes<N>,
 477      command: &Command<N>,
 478      consensus_fee_version: ConsensusFeeVersion,
 479  ) -> Result<u64> {
 480      let mapping_base_cost = match consensus_fee_version {
 481          ConsensusFeeVersion::V1 => MAPPING_BASE_COST_V1,
 482          ConsensusFeeVersion::V2 | ConsensusFeeVersion::V3 => MAPPING_BASE_COST_V2,
 483      };
 484  
 485      match command {
 486          Command::Instruction(Instruction::Abs(_)) => Ok(500),
 487          Command::Instruction(Instruction::AbsWrapped(_)) => Ok(500),
 488          Command::Instruction(Instruction::Add(_)) => Ok(500),
 489          Command::Instruction(Instruction::AddWrapped(_)) => Ok(500),
 490          Command::Instruction(Instruction::And(_)) => Ok(500),
 491          Command::Instruction(Instruction::AssertEq(_)) => Ok(500),
 492          Command::Instruction(Instruction::AssertNeq(_)) => Ok(500),
 493          Command::Instruction(Instruction::Async(_)) => bail!("'async' is not supported in finalize"),
 494          Command::Instruction(Instruction::Call(_)) => bail!("'call' is not supported in finalize"),
 495          Command::Instruction(Instruction::Cast(cast)) => match cast.cast_type() {
 496              CastType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
 497              CastType::Plaintext(plaintext_type) => Ok(plaintext_size_in_bytes(stack, plaintext_type)?
 498                  .saturating_mul(CAST_PER_BYTE_COST)
 499                  .saturating_add(CAST_BASE_COST)),
 500              CastType::GroupXCoordinate
 501              | CastType::GroupYCoordinate
 502              | CastType::Record(_)
 503              | CastType::ExternalRecord(_) => Ok(500),
 504          },
 505          Command::Instruction(Instruction::CastLossy(cast_lossy)) => match cast_lossy.cast_type() {
 506              CastType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
 507              CastType::Plaintext(plaintext_type) => Ok(plaintext_size_in_bytes(stack, plaintext_type)?
 508                  .saturating_mul(CAST_PER_BYTE_COST)
 509                  .saturating_add(CAST_BASE_COST)),
 510              CastType::GroupXCoordinate
 511              | CastType::GroupYCoordinate
 512              | CastType::Record(_)
 513              | CastType::ExternalRecord(_) => Ok(500),
 514          },
 515          Command::Instruction(Instruction::CommitBHP256(commit)) => {
 516              cost_in_size(stack, finalize_types, commit.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 517          }
 518          Command::Instruction(Instruction::CommitBHP512(commit)) => {
 519              cost_in_size(stack, finalize_types, commit.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 520          }
 521          Command::Instruction(Instruction::CommitBHP768(commit)) => {
 522              cost_in_size(stack, finalize_types, commit.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 523          }
 524          Command::Instruction(Instruction::CommitBHP1024(commit)) => {
 525              cost_in_size(stack, finalize_types, commit.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 526          }
 527          Command::Instruction(Instruction::CommitPED64(commit)) => {
 528              cost_in_size(stack, finalize_types, commit.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 529          }
 530          Command::Instruction(Instruction::CommitPED128(commit)) => {
 531              cost_in_size(stack, finalize_types, commit.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 532          }
 533          Command::Instruction(Instruction::DeserializeBits(deserialize)) => {
 534              Ok(plaintext_size_in_bytes(stack, &PlaintextType::Array(deserialize.operand_type().clone()))?
 535                  .saturating_mul(CAST_PER_BYTE_COST)
 536                  .saturating_add(CAST_BASE_COST))
 537          }
 538          Command::Instruction(Instruction::DeserializeBitsRaw(deserialize)) => {
 539              Ok(plaintext_size_in_bytes(stack, &PlaintextType::Array(deserialize.operand_type().clone()))?
 540                  .saturating_mul(CAST_PER_BYTE_COST)
 541                  .saturating_add(CAST_BASE_COST))
 542          }
 543          Command::Instruction(Instruction::Div(div)) => {
 544              // Ensure `div` has exactly two operands.
 545              ensure!(div.operands().len() == 2, "'div' must contain exactly 2 operands");
 546              // Retrieve the price by the operand type.
 547              match finalize_types.get_type_from_operand(stack, &div.operands()[0])? {
 548                  FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Field)) => Ok(1_500),
 549                  FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
 550                  FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'div' does not support arrays"),
 551                  FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'div' does not support structs"),
 552                  FinalizeType::Future(_) => bail!("'div' does not support futures"),
 553              }
 554          }
 555          Command::Instruction(Instruction::DivWrapped(_)) => Ok(500),
 556          Command::Instruction(Instruction::Double(_)) => Ok(500),
 557          Command::Instruction(Instruction::ECDSAVerifyDigest(_)) => Ok(ECDSA_VERIFY_BASE_COST),
 558          Command::Instruction(Instruction::ECDSAVerifyDigestEth(_)) => Ok(ECDSA_VERIFY_ETH_BASE_COST),
 559          Command::Instruction(Instruction::ECDSAVerifyKeccak256(ecdsa)) => {
 560              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 561          }
 562          Command::Instruction(Instruction::ECDSAVerifyKeccak256Raw(ecdsa)) => {
 563              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 564          }
 565          Command::Instruction(Instruction::ECDSAVerifyKeccak256Eth(ecdsa)) => {
 566              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 567          }
 568          Command::Instruction(Instruction::ECDSAVerifyKeccak384(ecdsa)) => {
 569              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 570          }
 571          Command::Instruction(Instruction::ECDSAVerifyKeccak384Raw(ecdsa)) => {
 572              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 573          }
 574          Command::Instruction(Instruction::ECDSAVerifyKeccak384Eth(ecdsa)) => {
 575              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 576          }
 577          Command::Instruction(Instruction::ECDSAVerifyKeccak512(ecdsa)) => {
 578              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 579          }
 580          Command::Instruction(Instruction::ECDSAVerifyKeccak512Raw(ecdsa)) => {
 581              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 582          }
 583          Command::Instruction(Instruction::ECDSAVerifyKeccak512Eth(ecdsa)) => {
 584              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 585          }
 586          Command::Instruction(Instruction::ECDSAVerifySha3_256(ecdsa)) => {
 587              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 588          }
 589          Command::Instruction(Instruction::ECDSAVerifySha3_256Raw(ecdsa)) => {
 590              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 591          }
 592          Command::Instruction(Instruction::ECDSAVerifySha3_256Eth(ecdsa)) => {
 593              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 594          }
 595          Command::Instruction(Instruction::ECDSAVerifySha3_384(ecdsa)) => {
 596              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 597          }
 598          Command::Instruction(Instruction::ECDSAVerifySha3_384Raw(ecdsa)) => {
 599              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 600          }
 601          Command::Instruction(Instruction::ECDSAVerifySha3_384Eth(ecdsa)) => {
 602              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 603          }
 604          Command::Instruction(Instruction::ECDSAVerifySha3_512(ecdsa)) => {
 605              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 606          }
 607          Command::Instruction(Instruction::ECDSAVerifySha3_512Raw(ecdsa)) => {
 608              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_BASE_COST)
 609          }
 610          Command::Instruction(Instruction::ECDSAVerifySha3_512Eth(ecdsa)) => {
 611              cost_in_size(stack, finalize_types, ecdsa.operands(), HASH_PER_BYTE_COST, ECDSA_VERIFY_ETH_BASE_COST)
 612          }
 613          Command::Instruction(Instruction::GreaterThan(_)) => Ok(500),
 614          Command::Instruction(Instruction::GreaterThanOrEqual(_)) => Ok(500),
 615          Command::Instruction(Instruction::HashBHP256(hash)) => {
 616              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 617          }
 618          Command::Instruction(Instruction::HashBHP256Raw(hash)) => {
 619              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 620          }
 621          Command::Instruction(Instruction::HashBHP512(hash)) => {
 622              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 623          }
 624          Command::Instruction(Instruction::HashBHP512Raw(hash)) => {
 625              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 626          }
 627          Command::Instruction(Instruction::HashBHP768(hash)) => {
 628              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 629          }
 630          Command::Instruction(Instruction::HashBHP768Raw(hash)) => {
 631              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 632          }
 633          Command::Instruction(Instruction::HashBHP1024(hash)) => {
 634              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 635          }
 636          Command::Instruction(Instruction::HashBHP1024Raw(hash)) => {
 637              cost_in_size(stack, finalize_types, hash.operands(), HASH_BHP_PER_BYTE_COST, HASH_BHP_BASE_COST)
 638          }
 639          Command::Instruction(Instruction::HashKeccak256(hash)) => {
 640              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 641          }
 642          Command::Instruction(Instruction::HashKeccak256Raw(hash)) => {
 643              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 644          }
 645          Command::Instruction(Instruction::HashKeccak256Native(hash)) => {
 646              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 647          }
 648          Command::Instruction(Instruction::HashKeccak256NativeRaw(hash)) => {
 649              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 650          }
 651          Command::Instruction(Instruction::HashKeccak384(hash)) => {
 652              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 653          }
 654          Command::Instruction(Instruction::HashKeccak384Raw(hash)) => {
 655              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 656          }
 657          Command::Instruction(Instruction::HashKeccak384Native(hash)) => {
 658              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 659          }
 660          Command::Instruction(Instruction::HashKeccak384NativeRaw(hash)) => {
 661              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 662          }
 663          Command::Instruction(Instruction::HashKeccak512(hash)) => {
 664              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 665          }
 666          Command::Instruction(Instruction::HashKeccak512Raw(hash)) => {
 667              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 668          }
 669          Command::Instruction(Instruction::HashKeccak512Native(hash)) => {
 670              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 671          }
 672          Command::Instruction(Instruction::HashKeccak512NativeRaw(hash)) => {
 673              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 674          }
 675          Command::Instruction(Instruction::HashPED64(hash)) => {
 676              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 677          }
 678          Command::Instruction(Instruction::HashPED64Raw(hash)) => {
 679              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 680          }
 681          Command::Instruction(Instruction::HashPED128(hash)) => {
 682              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 683          }
 684          Command::Instruction(Instruction::HashPED128Raw(hash)) => {
 685              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 686          }
 687          Command::Instruction(Instruction::HashPSD2(hash)) => {
 688              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 689          }
 690          Command::Instruction(Instruction::HashPSD2Raw(hash)) => {
 691              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 692          }
 693          Command::Instruction(Instruction::HashPSD4(hash)) => {
 694              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 695          }
 696          Command::Instruction(Instruction::HashPSD4Raw(hash)) => {
 697              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 698          }
 699          Command::Instruction(Instruction::HashPSD8(hash)) => {
 700              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 701          }
 702          Command::Instruction(Instruction::HashPSD8Raw(hash)) => {
 703              cost_in_size(stack, finalize_types, hash.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 704          }
 705          Command::Instruction(Instruction::HashSha3_256(hash)) => {
 706              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 707          }
 708          Command::Instruction(Instruction::HashSha3_256Raw(hash)) => {
 709              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 710          }
 711          Command::Instruction(Instruction::HashSha3_256Native(hash)) => {
 712              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 713          }
 714          Command::Instruction(Instruction::HashSha3_256NativeRaw(hash)) => {
 715              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 716          }
 717          Command::Instruction(Instruction::HashSha3_384(hash)) => {
 718              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 719          }
 720          Command::Instruction(Instruction::HashSha3_384Raw(hash)) => {
 721              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 722          }
 723          Command::Instruction(Instruction::HashSha3_384Native(hash)) => {
 724              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 725          }
 726          Command::Instruction(Instruction::HashSha3_384NativeRaw(hash)) => {
 727              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 728          }
 729          Command::Instruction(Instruction::HashSha3_512(hash)) => {
 730              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 731          }
 732          Command::Instruction(Instruction::HashSha3_512Raw(hash)) => {
 733              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 734          }
 735          Command::Instruction(Instruction::HashSha3_512Native(hash)) => {
 736              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 737          }
 738          Command::Instruction(Instruction::HashSha3_512NativeRaw(hash)) => {
 739              cost_in_size(stack, finalize_types, hash.operands(), HASH_PER_BYTE_COST, HASH_BASE_COST)
 740          }
 741          Command::Instruction(Instruction::HashManyPSD2(_)) => {
 742              bail!("`hash_many.psd2` is not supported in finalize")
 743          }
 744          Command::Instruction(Instruction::HashManyPSD4(_)) => {
 745              bail!("`hash_many.psd4` is not supported in finalize")
 746          }
 747          Command::Instruction(Instruction::HashManyPSD8(_)) => {
 748              bail!("`hash_many.psd8` is not supported in finalize")
 749          }
 750          Command::Instruction(Instruction::Inv(_)) => Ok(2_500),
 751          Command::Instruction(Instruction::IsEq(_)) => Ok(500),
 752          Command::Instruction(Instruction::IsNeq(_)) => Ok(500),
 753          Command::Instruction(Instruction::LessThan(_)) => Ok(500),
 754          Command::Instruction(Instruction::LessThanOrEqual(_)) => Ok(500),
 755          Command::Instruction(Instruction::Modulo(_)) => Ok(500),
 756          Command::Instruction(Instruction::Mul(mul)) => {
 757              // Ensure `mul` has exactly two operands.
 758              ensure!(mul.operands().len() == 2, "'mul' must contain exactly 2 operands");
 759              // Retrieve the price by operand type.
 760              match finalize_types.get_type_from_operand(stack, &mul.operands()[0])? {
 761                  FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Group)) => Ok(10_000),
 762                  FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Scalar)) => Ok(10_000),
 763                  FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
 764                  FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'mul' does not support arrays"),
 765                  FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'mul' does not support structs"),
 766                  FinalizeType::Future(_) => bail!("'mul' does not support futures"),
 767              }
 768          }
 769          Command::Instruction(Instruction::MulWrapped(_)) => Ok(500),
 770          Command::Instruction(Instruction::Nand(_)) => Ok(500),
 771          Command::Instruction(Instruction::Neg(_)) => Ok(500),
 772          Command::Instruction(Instruction::Nor(_)) => Ok(500),
 773          Command::Instruction(Instruction::Not(_)) => Ok(500),
 774          Command::Instruction(Instruction::Or(_)) => Ok(500),
 775          Command::Instruction(Instruction::Pow(pow)) => {
 776              // Ensure `pow` has at least one operand.
 777              ensure!(!pow.operands().is_empty(), "'pow' must contain at least 1 operand");
 778              // Retrieve the price by operand type.
 779              match finalize_types.get_type_from_operand(stack, &pow.operands()[0])? {
 780                  FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Field)) => Ok(1_500),
 781                  FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
 782                  FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'pow' does not support arrays"),
 783                  FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'pow' does not support structs"),
 784                  FinalizeType::Future(_) => bail!("'pow' does not support futures"),
 785              }
 786          }
 787          Command::Instruction(Instruction::PowWrapped(_)) => Ok(500),
 788          Command::Instruction(Instruction::Rem(_)) => Ok(500),
 789          Command::Instruction(Instruction::RemWrapped(_)) => Ok(500),
 790          Command::Instruction(Instruction::SerializeBits(serialize)) => {
 791              Ok(plaintext_size_in_bytes(stack, &PlaintextType::Array(serialize.destination_type().clone()))?
 792                  .saturating_mul(CAST_PER_BYTE_COST)
 793                  .saturating_add(CAST_BASE_COST))
 794          }
 795          Command::Instruction(Instruction::SerializeBitsRaw(serialize)) => {
 796              Ok(plaintext_size_in_bytes(stack, &PlaintextType::Array(serialize.destination_type().clone()))?
 797                  .saturating_mul(CAST_PER_BYTE_COST)
 798                  .saturating_add(CAST_BASE_COST))
 799          }
 800          Command::Instruction(Instruction::SignVerify(sign)) => {
 801              cost_in_size(stack, finalize_types, sign.operands(), HASH_PSD_PER_BYTE_COST, HASH_PSD_BASE_COST)
 802          }
 803          Command::Instruction(Instruction::Shl(_)) => Ok(500),
 804          Command::Instruction(Instruction::ShlWrapped(_)) => Ok(500),
 805          Command::Instruction(Instruction::Shr(_)) => Ok(500),
 806          Command::Instruction(Instruction::ShrWrapped(_)) => Ok(500),
 807          Command::Instruction(Instruction::Square(_)) => Ok(500),
 808          Command::Instruction(Instruction::SquareRoot(_)) => Ok(2_500),
 809          Command::Instruction(Instruction::Sub(_)) => Ok(500),
 810          Command::Instruction(Instruction::SubWrapped(_)) => Ok(500),
 811          Command::Instruction(Instruction::Ternary(_)) => Ok(500),
 812          Command::Instruction(Instruction::Xor(_)) => Ok(500),
 813          Command::Await(_) => Ok(500),
 814          Command::Contains(command) => {
 815              cost_in_size(stack, finalize_types, [command.key()], MAPPING_PER_BYTE_COST, mapping_base_cost)
 816          }
 817          Command::Get(command) => {
 818              cost_in_size(stack, finalize_types, [command.key()], MAPPING_PER_BYTE_COST, mapping_base_cost)
 819          }
 820          Command::GetOrUse(command) => {
 821              cost_in_size(stack, finalize_types, [command.key()], MAPPING_PER_BYTE_COST, mapping_base_cost)
 822          }
 823          Command::RandChaCha(_) => Ok(25_000),
 824          Command::Remove(_) => Ok(SET_BASE_COST),
 825          Command::Set(command) => {
 826              cost_in_size(stack, finalize_types, [command.key(), command.value()], SET_PER_BYTE_COST, SET_BASE_COST)
 827          }
 828          Command::BranchEq(_) | Command::BranchNeq(_) => Ok(500),
 829          Command::Position(_) => Ok(100),
 830      }
 831  }
 832  
 833  /// Returns the minimum number of microcredits required to run the constructor in the given stack.
 834  /// If a constructor does not exist, no cost is incurred.
 835  pub fn constructor_cost_in_microcredits_v2<N: Network>(stack: &Stack<N>) -> Result<u64> {
 836      match stack.program().constructor() {
 837          Some(constructor) => {
 838              // Get the constructor types.
 839              let constructor_types = stack.get_constructor_types()?;
 840              // Get the base cost of the constructor.
 841              let base_cost = constructor
 842                  .commands()
 843                  .iter()
 844                  .map(|command| cost_per_command(stack, &constructor_types, command, ConsensusFeeVersion::V2))
 845                  .try_fold(0u64, |acc, res| {
 846                      res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Constructor cost overflowed")))
 847                  })?;
 848              // Scale by the multiplier and divide by the ARC-0005 cost reduction factor.
 849              base_cost
 850                  .checked_mul(N::CONSTRUCTOR_FEE_MULTIPLIER)
 851                  .map(|result| result / N::ARC_0005_COMPUTE_DISCOUNT)
 852                  .ok_or(anyhow!("Constructor cost overflowed"))
 853          }
 854          None => Ok(0),
 855      }
 856  }
 857  
 858  /// Returns the minimum number of microcredits required to run the constructor in the given stack.
 859  /// If a constructor does not exist, no cost is incurred.
 860  pub fn constructor_cost_in_microcredits_v1<N: Network>(stack: &Stack<N>) -> Result<u64> {
 861      match stack.program().constructor() {
 862          Some(constructor) => {
 863              // Get the constructor types.
 864              let constructor_types = stack.get_constructor_types()?;
 865              // Get the base cost of the constructor.
 866              let base_cost = constructor
 867                  .commands()
 868                  .iter()
 869                  .map(|command| cost_per_command(stack, &constructor_types, command, ConsensusFeeVersion::V2))
 870                  .try_fold(0u64, |acc, res| {
 871                      res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Constructor cost overflowed")))
 872                  })?;
 873              // Scale by the multiplier and divide by the ARC-0005 cost reduction factor.
 874              base_cost.checked_mul(N::CONSTRUCTOR_FEE_MULTIPLIER).ok_or(anyhow!("Constructor cost overflowed"))
 875          }
 876          None => Ok(0),
 877      }
 878  }
 879  
 880  /// Returns the minimum number of microcredits required to run the finalize using the ARC-0005 cost reduction factor.
 881  pub fn cost_in_microcredits_v3<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
 882      cost_in_microcredits(stack, function_name, ConsensusFeeVersion::V3)
 883  }
 884  
 885  /// Returns the minimum number of microcredits required to run the finalize.
 886  pub fn cost_in_microcredits_v2<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
 887      cost_in_microcredits(stack, function_name, ConsensusFeeVersion::V2)
 888  }
 889  
 890  /// Returns the minimum number of microcredits required to run the finalize (deprecated).
 891  pub fn cost_in_microcredits_v1<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
 892      cost_in_microcredits(stack, function_name, ConsensusFeeVersion::V1)
 893  }
 894  
 895  // A helper function to compute the cost in microcredits for a given function.
 896  fn cost_in_microcredits<N: Network>(
 897      stack: &Stack<N>,
 898      function_name: &Identifier<N>,
 899      consensus_fee_version: ConsensusFeeVersion,
 900  ) -> Result<u64> {
 901      // Initialize the base cost.
 902      let mut finalize_cost = 0u64;
 903      // Initialize a queue of finalize blocks to tally.
 904      let mut finalizes = vec![(StackRef::Internal(stack), *function_name)];
 905      // Initialize a counter for the number of finalize blocks seen.
 906      let mut num_finalizes = 1;
 907      // Get the quotient for the cost reduction factor.
 908      let quotient = match consensus_fee_version {
 909          ConsensusFeeVersion::V1 | ConsensusFeeVersion::V2 => 1,
 910          ConsensusFeeVersion::V3 => N::ARC_0005_COMPUTE_DISCOUNT,
 911      };
 912      // Iterate over the finalize blocks.
 913      while let Some((stack_ref, function_name)) = finalizes.pop() {
 914          // Ensure that the number of finalize blocks does not exceed the maximum.
 915          // Note that one transition is reserved for the fee.
 916          ensure!(
 917              num_finalizes < Transaction::<N>::MAX_TRANSITIONS,
 918              "The number of finalize blocks must be less than '{}'",
 919              Transaction::<N>::MAX_TRANSITIONS
 920          );
 921          // Get the finalize logic. If the function does not have a finalize scope then no cost is incurred.
 922          if let Some(finalize) = stack_ref.get_function_ref(&function_name)?.finalize_logic() {
 923              // Queue the futures to be tallied.
 924              for input in finalize.inputs() {
 925                  if let FinalizeType::Future(future) = input.finalize_type() {
 926                      // Increment the number of finalize blocks seen.
 927                      num_finalizes += 1;
 928                      // If the locator matches the program ID of the provided stack, use it directly.
 929                      // Otherwise, retrieve the external stack.
 930                      let stack = if future.program_id() == stack.program().id() {
 931                          StackRef::Internal(stack)
 932                      } else {
 933                          StackRef::External(stack_ref.get_external_stack(future.program_id())?)
 934                      };
 935                      // Queue the future.
 936                      finalizes.push((stack, *future.resource()));
 937                  }
 938              }
 939              // Get the finalize types.
 940              let finalize_types = stack_ref.get_finalize_types(finalize.name())?;
 941              // Iterate over the commands in the finalize block.
 942              for command in finalize.commands() {
 943                  // Sum the cost of all commands in the current future into the total running cost.
 944                  finalize_cost = finalize_cost
 945                      .checked_add(cost_per_command(&stack_ref, &finalize_types, command, consensus_fee_version)?)
 946                      .ok_or(anyhow!("Finalize cost overflowed"))?;
 947              }
 948          }
 949      }
 950      Ok(finalize_cost / quotient)
 951  }
 952  
 953  #[cfg(test)]
 954  mod tests {
 955      use super::*;
 956      use crate::test_helpers::get_execution;
 957      use circuit::{Aleo, AleoCanaryV0, AleoTestnetV0, AleoV0};
 958  
 959      use alphavm_synthesizer_program::Program;
 960      use console::{
 961          network::{CanaryV0, MainnetV0, TestnetV0},
 962          types::Address,
 963      };
 964  
 965      // Test program with two functions just below and above the size threshold.
 966      const SIZE_BOUNDARY_PROGRAM: &str = r#"
 967  program size_boundary.alpha;
 968  
 969  function under_five_thousand:
 970      input r0 as group.public;
 971      cast r0 r0 r0 r0 r0 r0 r0 r0 r0 into r1 as [group; 9u32];
 972      cast r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 into r2 as [[group; 9u32]; 10u32];
 973      cast r0 r0 r0 r0 r0 r0 r0 into r3 as [group; 7u32];
 974      output r2 as [[group; 9u32]; 10u32].public;
 975      output r3 as [group; 7u32].public;
 976  
 977  function over_five_thousand:
 978      input r0 as group.public;
 979      cast r0 r0 r0 r0 r0 r0 r0 r0 r0 into r1 as [group; 9u32];
 980      cast r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 into r2 as [[group; 9u32]; 10u32];
 981      cast r0 r0 r0 r0 r0 r0 r0 into r3 as [group; 7u32];
 982      output r2 as [[group; 9u32]; 10u32].public;
 983      output r3 as [group; 7u32].public;
 984      output 5u64 as u64.public;
 985      "#;
 986      // Cost for a program +1 byte above the threshold.
 987      const STORAGE_COST_ABOVE_THRESHOLD: u64 = 5002;
 988      // Storage cost for an execution transaction at the maximum transaction size.
 989      const STORAGE_COST_MAX: u64 = 3_276_800;
 990  
 991      fn test_storage_cost_bounds<N: Network>() {
 992          // Calculate the bounds directly above and below the size threshold.
 993          let threshold = N::EXECUTION_STORAGE_PENALTY_THRESHOLD;
 994          let threshold_lower_offset = threshold.saturating_sub(1);
 995          let threshold_upper_offset = threshold.saturating_add(1);
 996  
 997          // Test the storage cost bounds.
 998          assert_eq!(execution_storage_cost::<N>(0), 0);
 999          assert_eq!(execution_storage_cost::<N>(1), 1);
1000          assert_eq!(execution_storage_cost::<N>(threshold_lower_offset), threshold_lower_offset);
1001          assert_eq!(execution_storage_cost::<N>(threshold), threshold);
1002          assert_eq!(execution_storage_cost::<N>(threshold_upper_offset), STORAGE_COST_ABOVE_THRESHOLD);
1003          assert_eq!(execution_storage_cost::<N>(N::MAX_TRANSACTION_SIZE as u64), STORAGE_COST_MAX);
1004      }
1005  
1006      #[test]
1007      fn test_storage_cost_bounds_for_all_networks() {
1008          test_storage_cost_bounds::<CanaryV0>();
1009          test_storage_cost_bounds::<MainnetV0>();
1010          test_storage_cost_bounds::<TestnetV0>();
1011      }
1012  
1013      #[test]
1014      fn test_storage_costs_compute_correctly() {
1015          // Test the storage cost of an execution.
1016          let threshold = MainnetV0::EXECUTION_STORAGE_PENALTY_THRESHOLD;
1017  
1018          // Test the cost of an execution.
1019          let mut process = Process::load().unwrap();
1020  
1021          // Get the program.
1022          let program = Program::from_str(SIZE_BOUNDARY_PROGRAM).unwrap();
1023  
1024          // Get the program identifiers.
1025          let under_5000 = Identifier::from_str("under_five_thousand").unwrap();
1026          let over_5000 = Identifier::from_str("over_five_thousand").unwrap();
1027  
1028          // Get execution and cost data.
1029          let execution_under_5000 = get_execution(&mut process, &program, &under_5000, ["2group"].into_iter());
1030          let execution_size_under_5000 = execution_under_5000.size_in_bytes().unwrap();
1031          let (_, (storage_cost_under_5000, _)) =
1032              execution_cost_v3(&process, &execution_under_5000, execution_size_under_5000).unwrap();
1033          let execution_over_5000 = get_execution(&mut process, &program, &over_5000, ["2group"].into_iter());
1034          let execution_size_over_5000 = execution_over_5000.size_in_bytes().unwrap();
1035          let (_, (storage_cost_over_5000, _)) =
1036              execution_cost_v3(&process, &execution_over_5000, execution_size_over_5000).unwrap();
1037  
1038          // Ensure the sizes are below and above the threshold respectively.
1039          assert!(execution_size_under_5000 < threshold);
1040          assert!(execution_size_over_5000 > threshold);
1041  
1042          // Ensure storage costs compute correctly.
1043          assert_eq!(storage_cost_under_5000, execution_storage_cost::<MainnetV0>(execution_size_under_5000));
1044          assert_eq!(storage_cost_over_5000, execution_storage_cost::<MainnetV0>(execution_size_over_5000));
1045      }
1046  
1047      #[test]
1048      fn test_deployment_cost_with_constructors() {
1049          // A helper to run the test.
1050          fn run_test<N: Network, A: Aleo<Network = N>>() {
1051              let process = Process::<N>::load().unwrap();
1052              let rng = &mut TestRng::default();
1053  
1054              // Define the programs.
1055              let program_0 = Program::from_str(
1056                  r"
1057  program program_with_constructor.alpha;
1058  
1059  constructor:
1060      assert.eq true true;
1061  
1062  mapping foo:
1063      key as field.public;
1064      value as field.public;
1065  
1066  function dummy:",
1067              )
1068              .unwrap();
1069  
1070              let program_1 = Program::from_str(
1071                  r"
1072  program program_with_constructor.alpha;
1073  
1074  constructor:
1075      assert.eq edition 0u16;
1076  
1077  mapping foo:
1078      key as field.public;
1079      value as field.public;
1080  
1081  function dummy:",
1082              )
1083              .unwrap();
1084  
1085              let program_2 = Program::from_str(
1086                  r"
1087  program program_with_constructor.alpha;
1088  
1089  constructor:
1090      get foo[0field] into r0;
1091  
1092  mapping foo:
1093      key as field.public;
1094      value as field.public;
1095  
1096  function dummy:",
1097              )
1098              .unwrap();
1099  
1100              let program_3 = Program::from_str(
1101                  r"
1102  program program_with_constructor.alpha;
1103  
1104  constructor:
1105      set 0field into foo[0field];
1106  
1107  mapping foo:
1108      key as field.public;
1109      value as field.public;
1110  
1111  function dummy:",
1112              )
1113              .unwrap();
1114  
1115              // Verify the deployment costs.
1116              let mut deployment_0 = process.deploy::<A, _>(&program_0, rng).unwrap();
1117              deployment_0.set_program_checksum_raw(Some(deployment_0.program().to_checksum()));
1118              deployment_0.set_program_owner_raw(Some(Address::rand(rng)));
1119              let expected_storage_cost = 879000;
1120              let expected_synthesis_cost = 603500;
1121              let expected_constructor_cost = 50000;
1122              let expected_namespace_cost = 1000000;
1123              let expected_total_cost =
1124                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1125              assert_eq!(
1126                  deployment_cost_v1(&process, &deployment_0).unwrap(),
1127                  (
1128                      expected_total_cost,
1129                      (
1130                          expected_storage_cost,
1131                          expected_synthesis_cost,
1132                          expected_constructor_cost,
1133                          expected_namespace_cost
1134                      )
1135                  )
1136              );
1137              let expected_synthesis_cost = expected_synthesis_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1138              let expected_constructor_cost = expected_constructor_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1139              let expected_total_cost =
1140                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1141              assert_eq!(
1142                  deployment_cost_v2(&process, &deployment_0).unwrap(),
1143                  (
1144                      expected_total_cost,
1145                      (
1146                          expected_storage_cost,
1147                          expected_synthesis_cost,
1148                          expected_constructor_cost,
1149                          expected_namespace_cost
1150                      )
1151                  )
1152              );
1153  
1154              let mut deployment_1 = process.deploy::<A, _>(&program_1, rng).unwrap();
1155              deployment_1.set_program_checksum_raw(Some(deployment_1.program().to_checksum()));
1156              deployment_1.set_program_owner_raw(Some(Address::rand(rng)));
1157              let expected_storage_cost = 878000;
1158              let expected_synthesis_cost = 603500;
1159              let expected_constructor_cost = 50000;
1160              let expected_namespace_cost = 1000000;
1161              let expected_total_cost =
1162                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1163              assert_eq!(
1164                  deployment_cost_v1(&process, &deployment_1).unwrap(),
1165                  (
1166                      expected_total_cost,
1167                      (
1168                          expected_storage_cost,
1169                          expected_synthesis_cost,
1170                          expected_constructor_cost,
1171                          expected_namespace_cost
1172                      )
1173                  )
1174              );
1175              let expected_synthesis_cost = expected_synthesis_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1176              let expected_constructor_cost = expected_constructor_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1177              let expected_total_cost =
1178                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1179              assert_eq!(
1180                  deployment_cost_v2(&process, &deployment_1).unwrap(),
1181                  (
1182                      expected_total_cost,
1183                      (
1184                          expected_storage_cost,
1185                          expected_synthesis_cost,
1186                          expected_constructor_cost,
1187                          expected_namespace_cost
1188                      )
1189                  )
1190              );
1191  
1192              let mut deployment_2 = process.deploy::<A, _>(&program_2, rng).unwrap();
1193              deployment_2.set_program_checksum_raw(Some(deployment_2.program().to_checksum()));
1194              deployment_2.set_program_owner_raw(Some(Address::rand(rng)));
1195              let expected_storage_cost = 911000;
1196              let expected_synthesis_cost = 603500;
1197              let expected_constructor_cost = 182000;
1198              let expected_namespace_cost = 1000000;
1199              let expected_total_cost =
1200                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1201              assert_eq!(
1202                  deployment_cost_v1(&process, &deployment_2).unwrap(),
1203                  (
1204                      expected_total_cost,
1205                      (
1206                          expected_storage_cost,
1207                          expected_synthesis_cost,
1208                          expected_constructor_cost,
1209                          expected_namespace_cost
1210                      )
1211                  )
1212              );
1213              let expected_synthesis_cost = expected_synthesis_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1214              let expected_constructor_cost = expected_constructor_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1215              let expected_total_cost =
1216                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1217              assert_eq!(
1218                  deployment_cost_v2(&process, &deployment_2).unwrap(),
1219                  (
1220                      expected_total_cost,
1221                      (
1222                          expected_storage_cost,
1223                          expected_synthesis_cost,
1224                          expected_constructor_cost,
1225                          expected_namespace_cost
1226                      )
1227                  )
1228              );
1229  
1230              let mut deployment_3 = process.deploy::<A, _>(&program_3, rng).unwrap();
1231              deployment_3.set_program_checksum_raw(Some(deployment_3.program().to_checksum()));
1232              deployment_3.set_program_owner_raw(Some(Address::rand(rng)));
1233              let expected_storage_cost = 943000;
1234              let expected_synthesis_cost = 603500;
1235              let expected_constructor_cost = 1640000;
1236              let expected_namespace_cost = 1000000;
1237              let expected_total_cost =
1238                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1239              assert_eq!(
1240                  deployment_cost_v1(&process, &deployment_3).unwrap(),
1241                  (
1242                      expected_total_cost,
1243                      (
1244                          expected_storage_cost,
1245                          expected_synthesis_cost,
1246                          expected_constructor_cost,
1247                          expected_namespace_cost
1248                      )
1249                  )
1250              );
1251              let expected_synthesis_cost = expected_synthesis_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1252              let expected_constructor_cost = expected_constructor_cost / N::ARC_0005_COMPUTE_DISCOUNT;
1253              let expected_total_cost =
1254                  expected_storage_cost + expected_synthesis_cost + expected_constructor_cost + expected_namespace_cost;
1255              assert_eq!(
1256                  deployment_cost_v2(&process, &deployment_3).unwrap(),
1257                  (
1258                      expected_total_cost,
1259                      (
1260                          expected_storage_cost,
1261                          expected_synthesis_cost,
1262                          expected_constructor_cost,
1263                          expected_namespace_cost
1264                      )
1265                  )
1266              );
1267          }
1268  
1269          // Run the tests for all networks.
1270          run_test::<CanaryV0, AleoCanaryV0>();
1271          run_test::<MainnetV0, AleoV0>();
1272          run_test::<TestnetV0, AleoTestnetV0>();
1273      }
1274  }