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 }