1use crate::util::batch::GroveDbOpBatch;
2use grovedb_costs::storage_cost::removal::Identifier;
3use grovedb_costs::storage_cost::removal::StorageRemovedBytes::{
4 BasicStorageRemoval, NoStorageRemoval, SectionedStorageRemoval,
5};
6use std::collections::BTreeMap;
7
8use enum_map::Enum;
9use grovedb::batch::key_info::KeyInfo;
10use grovedb::batch::KeyInfoPath;
11use grovedb::element::reference_path::ReferencePathType;
12use grovedb::element::MaxReferenceHop;
13use grovedb::{batch::QualifiedGroveDbOp, Element, ElementFlags, TreeType};
14use grovedb_costs::OperationCost;
15use itertools::Itertools;
16
17use crate::error::drive::DriveError;
18use crate::error::Error;
19use crate::fees::get_overflow_error;
20use crate::fees::op::LowLevelDriveOperation::{
21 CalculatedCostOperation, FunctionOperation, GroveOperation, PreCalculatedFeeResult,
22};
23use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods;
24use crate::util::storage_flags::StorageFlags;
25use dpp::block::epoch::Epoch;
26use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
27use dpp::fee::fee_result::refunds::FeeRefunds;
28use dpp::fee::fee_result::FeeResult;
29use dpp::fee::Credits;
30use platform_version::version::fee::FeeVersion;
31
32#[derive(Debug, Enum)]
34pub enum BaseOp {
35 Stop,
37 Add,
39 Mul,
41 Sub,
43 Div,
45 Sdiv,
47 Mod,
49 Smod,
51 Addmod,
53 Mulmod,
55 Signextend,
57 Lt,
59 Gt,
61 Slt,
63 Sgt,
65 Eq,
67 Iszero,
69 And,
71 Or,
73 Xor,
75 Not,
77 Byte,
79}
80
81impl BaseOp {
82 pub fn cost(&self) -> u64 {
84 match self {
85 BaseOp::Stop => 0,
86 BaseOp::Add => 12,
87 BaseOp::Mul => 20,
88 BaseOp::Sub => 12,
89 BaseOp::Div => 20,
90 BaseOp::Sdiv => 20,
91 BaseOp::Mod => 20,
92 BaseOp::Smod => 20,
93 BaseOp::Addmod => 32,
94 BaseOp::Mulmod => 32,
95 BaseOp::Signextend => 20,
96 BaseOp::Lt => 12,
97 BaseOp::Gt => 12,
98 BaseOp::Slt => 12,
99 BaseOp::Sgt => 12,
100 BaseOp::Eq => 12,
101 BaseOp::Iszero => 12,
102 BaseOp::And => 12,
103 BaseOp::Or => 12,
104 BaseOp::Xor => 12,
105 BaseOp::Not => 12,
106 BaseOp::Byte => 12,
107 }
108 }
109}
110
111#[derive(Debug, Enum, PartialEq, Eq)]
113pub enum HashFunction {
114 Sha256RipeMD160,
116 Sha256,
118 Sha256_2,
120 Blake3,
122}
123
124impl HashFunction {
125 fn block_size(&self) -> u16 {
126 match self {
127 HashFunction::Sha256 => 64,
128 HashFunction::Sha256_2 => 64,
129 HashFunction::Blake3 => 64,
130 HashFunction::Sha256RipeMD160 => 64,
131 }
132 }
133
134 fn rounds(&self) -> u16 {
135 match self {
136 HashFunction::Sha256 => 1,
137 HashFunction::Sha256_2 => 2,
138 HashFunction::Blake3 => 1,
139 HashFunction::Sha256RipeMD160 => 1,
140 }
141 }
142
143 fn block_cost(&self, fee_version: &FeeVersion) -> u64 {
144 match self {
145 HashFunction::Sha256 => fee_version.hashing.sha256_per_block,
146 HashFunction::Sha256_2 => fee_version.hashing.sha256_per_block,
147 HashFunction::Blake3 => fee_version.hashing.blake3_per_block,
148 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_per_block,
149 }
150 }
151
152 fn base_cost(&self, fee_version: &FeeVersion) -> u64 {
153 match self {
154 HashFunction::Sha256 => fee_version.hashing.single_sha256_base,
155 HashFunction::Sha256_2 => fee_version.hashing.single_sha256_base,
158 HashFunction::Blake3 => fee_version.hashing.blake3_base,
159 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_ripe_md160_base,
160 }
161 }
162}
163
164#[derive(Debug, PartialEq, Eq)]
166pub struct FunctionOp {
167 pub(crate) hash: HashFunction,
169 pub(crate) rounds: u32,
171}
172
173impl FunctionOp {
174 fn cost(&self, fee_version: &FeeVersion) -> Credits {
176 let block_cost = (self.rounds as u64).saturating_mul(self.hash.block_cost(fee_version));
177 self.hash.base_cost(fee_version).saturating_add(block_cost)
178 }
179
180 pub fn new_with_round_count(hash: HashFunction, rounds: u32) -> Self {
183 FunctionOp { hash, rounds }
184 }
185
186 pub fn new_with_byte_count(hash: HashFunction, byte_count: u16) -> Self {
189 let blocks = byte_count / hash.block_size() + 1;
190 let rounds = blocks + hash.rounds() - 1;
191 FunctionOp {
192 hash,
193 rounds: rounds as u32,
194 }
195 }
196}
197
198#[derive(Debug, Eq, PartialEq)]
200pub enum LowLevelDriveOperation {
201 GroveOperation(QualifiedGroveDbOp),
203 FunctionOperation(FunctionOp),
205 CalculatedCostOperation(OperationCost),
207 PreCalculatedFeeResult(FeeResult),
209}
210
211impl LowLevelDriveOperation {
212 pub fn consume_to_fees_v0(
215 drive_operations: Vec<LowLevelDriveOperation>,
216 epoch: &Epoch,
217 epochs_per_era: u16,
218 fee_version: &FeeVersion,
219 previous_fee_versions: Option<&CachedEpochIndexFeeVersions>,
220 ) -> Result<Vec<FeeResult>, Error> {
221 drive_operations
222 .into_iter()
223 .map(|operation| match operation {
224 PreCalculatedFeeResult(f) => Ok(f),
225 FunctionOperation(op) => Ok(FeeResult {
226 processing_fee: op.cost(fee_version),
227 ..Default::default()
228 }),
229 _ => {
230 let cost = operation.operation_cost()?;
231 let storage_fee = cost.storage_cost.added_bytes as u64 * fee_version.storage.storage_disk_usage_credit_per_byte;
234 let processing_fee = cost.ephemeral_cost(fee_version)?;
235 let (fee_refunds, removed_bytes_from_system) =
236 match cost.storage_cost.removed_bytes {
237 NoStorageRemoval => (FeeRefunds::default(), 0),
238 BasicStorageRemoval(amount) => {
239 (FeeRefunds::default(), amount)
241 }
242 SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => {
243
244 let system_amount = removal_per_epoch_by_identifier
245 .remove(&Identifier::default())
246 .map_or(0, |a| a.values().sum());
247 if fee_version.fee_version_number == 1 {
248 (
249 FeeRefunds::from_storage_removal(
250 removal_per_epoch_by_identifier,
251 epoch.index,
252 epochs_per_era,
253 &BTreeMap::default(),
254 )?,
255 system_amount,
256 )
257 } else {
258 let previous_fee_versions = previous_fee_versions.ok_or(Error::Drive(DriveError::CorruptedCodeExecution("expected previous epoch index fee versions to be able to offer refunds")))?;
259 (
260 FeeRefunds::from_storage_removal(
261 removal_per_epoch_by_identifier,
262 epoch.index,
263 epochs_per_era,
264 previous_fee_versions,
265 )?,
266 system_amount,
267 )
268 }
269 }
270 };
271 Ok(FeeResult {
272 storage_fee,
273 processing_fee,
274 fee_refunds,
275 removed_bytes_from_system,
276 })
277 }
278 })
279 .collect()
280 }
281
282 pub fn operation_cost(self) -> Result<OperationCost, Error> {
284 match self {
285 GroveOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
286 "grove operations must be executed, not directly transformed to costs",
287 ))),
288 CalculatedCostOperation(c) => Ok(c),
289 PreCalculatedFeeResult(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
290 "pre calculated fees should not be requested by operation costs",
291 ))),
292 FunctionOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
293 "function operations should not be requested by operation costs",
294 ))),
295 }
296 }
297
298 pub fn combine_cost_operations(operations: &[LowLevelDriveOperation]) -> OperationCost {
300 let mut cost = OperationCost::default();
301 operations.iter().for_each(|op| {
302 if let CalculatedCostOperation(operation_cost) = op {
303 cost += operation_cost.clone()
304 }
305 });
306 cost
307 }
308
309 pub fn grovedb_operations_batch(
311 insert_operations: &[LowLevelDriveOperation],
312 ) -> GroveDbOpBatch {
313 let operations = insert_operations
314 .iter()
315 .filter_map(|op| match op {
316 GroveOperation(grovedb_op) => Some(grovedb_op.clone()),
317 _ => None,
318 })
319 .collect();
320 GroveDbOpBatch::from_operations(operations)
321 }
322
323 pub fn grovedb_operations_batch_consume(
325 insert_operations: Vec<LowLevelDriveOperation>,
326 ) -> GroveDbOpBatch {
327 let operations = insert_operations
328 .into_iter()
329 .filter_map(|op| match op {
330 GroveOperation(grovedb_op) => Some(grovedb_op),
331 _ => None,
332 })
333 .collect();
334 GroveDbOpBatch::from_operations(operations)
335 }
336
337 pub fn grovedb_operations_batch_consume_with_leftovers(
339 insert_operations: Vec<LowLevelDriveOperation>,
340 ) -> (GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
341 let (grove_operations, other_operations): (Vec<_>, Vec<_>) =
342 insert_operations.into_iter().partition_map(|op| match op {
343 GroveOperation(grovedb_op) => itertools::Either::Left(grovedb_op),
344 _ => itertools::Either::Right(op),
345 });
346
347 (
348 GroveDbOpBatch::from_operations(grove_operations),
349 other_operations,
350 )
351 }
352
353 pub fn grovedb_operations_consume(
355 insert_operations: Vec<LowLevelDriveOperation>,
356 ) -> Vec<QualifiedGroveDbOp> {
357 insert_operations
358 .into_iter()
359 .filter_map(|op| match op {
360 GroveOperation(grovedb_op) => Some(grovedb_op),
361 _ => None,
362 })
363 .collect()
364 }
365
366 pub fn for_known_path_key_empty_tree(
368 path: Vec<Vec<u8>>,
369 key: Vec<u8>,
370 storage_flags: Option<&StorageFlags>,
371 ) -> Self {
372 let tree = match storage_flags {
373 Some(storage_flags) => {
374 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
375 }
376 None => Element::empty_tree(),
377 };
378
379 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
380 }
381
382 pub fn for_known_path_key_empty_sum_tree(
384 path: Vec<Vec<u8>>,
385 key: Vec<u8>,
386 storage_flags: Option<&StorageFlags>,
387 ) -> Self {
388 let tree = match storage_flags {
389 Some(storage_flags) => {
390 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
391 }
392 None => Element::empty_sum_tree(),
393 };
394
395 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
396 }
397
398 pub fn for_known_path_key_empty_big_sum_tree(
400 path: Vec<Vec<u8>>,
401 key: Vec<u8>,
402 storage_flags: Option<&StorageFlags>,
403 ) -> Self {
404 let tree = match storage_flags {
405 Some(storage_flags) => {
406 Element::new_big_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
407 }
408 None => Element::empty_big_sum_tree(),
409 };
410
411 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
412 }
413
414 pub fn for_known_path_key_empty_count_tree(
416 path: Vec<Vec<u8>>,
417 key: Vec<u8>,
418 storage_flags: Option<&StorageFlags>,
419 ) -> Self {
420 let tree = match storage_flags {
421 Some(storage_flags) => {
422 Element::new_count_tree_with_flags(None, storage_flags.to_some_element_flags())
423 }
424 None => Element::empty_count_tree(),
425 };
426
427 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
428 }
429
430 pub fn for_known_path_key_empty_count_sum_tree(
432 path: Vec<Vec<u8>>,
433 key: Vec<u8>,
434 storage_flags: Option<&StorageFlags>,
435 ) -> Self {
436 let tree = match storage_flags {
437 Some(storage_flags) => {
438 Element::new_count_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
439 }
440 None => Element::new_count_sum_tree(None),
441 };
442
443 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
444 }
445
446 pub fn for_known_path_key_empty_non_counted_normal_tree(
454 path: Vec<Vec<u8>>,
455 key: Vec<u8>,
456 storage_flags: Option<&StorageFlags>,
457 ) -> Self {
458 Self::for_known_path_key_empty_non_counted_tree(
459 path,
460 key,
461 TreeType::NormalTree,
462 storage_flags,
463 )
464 .expect("NormalTree NonCounted wrapping never fails")
465 }
466
467 pub fn for_known_path_key_empty_non_counted_tree(
484 path: Vec<Vec<u8>>,
485 key: Vec<u8>,
486 tree_type: TreeType,
487 storage_flags: Option<&StorageFlags>,
488 ) -> Result<Self, Error> {
489 let element_flags = storage_flags.map(|s| s.to_element_flags());
490 let inner = match tree_type {
491 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
492 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
493 TreeType::ProvableCountTree => {
494 Element::empty_provable_count_tree_with_flags(element_flags)
495 }
496 _ => {
497 return Err(Error::Drive(DriveError::NotSupported(
498 "NonCounted-wrapping is only supported for NormalTree, CountTree, and ProvableCountTree",
499 )));
500 }
501 };
502 let tree = Element::new_non_counted(inner)
503 .expect("new_non_counted only fails when wrapping another NonCounted");
504 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
505 path, key, tree,
506 ))
507 }
508
509 pub fn for_known_path_key_empty_provable_count_tree(
511 path: Vec<Vec<u8>>,
512 key: Vec<u8>,
513 storage_flags: Option<&StorageFlags>,
514 ) -> Self {
515 let tree = match storage_flags {
516 Some(storage_flags) => Element::new_provable_count_tree_with_flags(
517 None,
518 storage_flags.to_some_element_flags(),
519 ),
520 None => Element::empty_provable_count_tree(),
521 };
522
523 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
524 }
525
526 pub fn for_estimated_path_key_empty_tree(
528 path: KeyInfoPath,
529 key: KeyInfo,
530 storage_flags: Option<&StorageFlags>,
531 ) -> Self {
532 let tree = match storage_flags {
533 Some(storage_flags) => {
534 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
535 }
536 None => Element::empty_tree(),
537 };
538
539 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
540 }
541
542 pub fn for_estimated_path_key_empty_sum_tree(
544 path: KeyInfoPath,
545 key: KeyInfo,
546 storage_flags: Option<&StorageFlags>,
547 ) -> Self {
548 let tree = match storage_flags {
549 Some(storage_flags) => {
550 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
551 }
552 None => Element::empty_sum_tree(),
553 };
554
555 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
556 }
557
558 pub fn for_estimated_path_key_empty_count_tree(
560 path: KeyInfoPath,
561 key: KeyInfo,
562 storage_flags: Option<&StorageFlags>,
563 ) -> Self {
564 let tree = match storage_flags {
565 Some(storage_flags) => {
566 Element::empty_count_tree_with_flags(storage_flags.to_some_element_flags())
567 }
568 None => Element::empty_count_tree(),
569 };
570
571 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
572 }
573
574 pub fn for_estimated_path_key_empty_provable_count_tree(
576 path: KeyInfoPath,
577 key: KeyInfo,
578 storage_flags: Option<&StorageFlags>,
579 ) -> Self {
580 let tree = match storage_flags {
581 Some(storage_flags) => {
582 Element::empty_provable_count_tree_with_flags(storage_flags.to_some_element_flags())
583 }
584 None => Element::empty_provable_count_tree(),
585 };
586
587 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
588 }
589
590 pub fn insert_for_known_path_key_element(
592 path: Vec<Vec<u8>>,
593 key: Vec<u8>,
594 element: Element,
595 ) -> Self {
596 GroveOperation(QualifiedGroveDbOp::insert_or_replace_op(path, key, element))
597 }
598
599 pub fn replace_for_known_path_key_element(
601 path: Vec<Vec<u8>>,
602 key: Vec<u8>,
603 element: Element,
604 ) -> Self {
605 GroveOperation(QualifiedGroveDbOp::replace_op(path, key, element))
606 }
607
608 pub fn patch_for_known_path_key_element(
611 path: Vec<Vec<u8>>,
612 key: Vec<u8>,
613 element: Element,
614 change_in_bytes: i32,
615 ) -> Self {
616 GroveOperation(QualifiedGroveDbOp::patch_op(
617 path,
618 key,
619 element,
620 change_in_bytes,
621 ))
622 }
623
624 pub fn insert_for_estimated_path_key_element(
626 path: KeyInfoPath,
627 key: KeyInfo,
628 element: Element,
629 ) -> Self {
630 GroveOperation(QualifiedGroveDbOp::insert_estimated_op(path, key, element))
631 }
632
633 pub fn replace_for_estimated_path_key_element(
635 path: KeyInfoPath,
636 key: KeyInfo,
637 element: Element,
638 ) -> Self {
639 GroveOperation(QualifiedGroveDbOp::replace_estimated_op(path, key, element))
640 }
641
642 pub fn refresh_reference_for_known_path_key_reference_info(
644 path: Vec<Vec<u8>>,
645 key: Vec<u8>,
646 reference_path_type: ReferencePathType,
647 max_reference_hop: MaxReferenceHop,
648 flags: Option<ElementFlags>,
649 trust_refresh_reference: bool,
650 ) -> Self {
651 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
652 path,
653 key,
654 reference_path_type,
655 max_reference_hop,
656 flags,
657 trust_refresh_reference,
658 ))
659 }
660}
661
662pub trait LowLevelDriveOperationTreeTypeConverter {
664 fn empty_tree_operation_for_known_path_key(
666 &self,
667 path: Vec<Vec<u8>>,
668 key: Vec<u8>,
669 storage_flags: Option<&StorageFlags>,
670 ) -> Result<LowLevelDriveOperation, Error>;
671}
672
673impl LowLevelDriveOperationTreeTypeConverter for TreeType {
674 fn empty_tree_operation_for_known_path_key(
676 &self,
677 path: Vec<Vec<u8>>,
678 key: Vec<u8>,
679 storage_flags: Option<&StorageFlags>,
680 ) -> Result<LowLevelDriveOperation, Error> {
681 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
682 let element = match self {
683 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
684 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
685 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
686 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
687 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
688 TreeType::ProvableCountTree => {
689 Element::empty_provable_count_tree_with_flags(element_flags)
690 }
691 TreeType::ProvableCountSumTree => {
692 Element::empty_provable_count_sum_tree_with_flags(element_flags)
693 }
694 TreeType::CommitmentTree(chunk_power) => {
695 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
696 }
697 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
698 TreeType::BulkAppendTree(chunk_power) => {
699 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
700 }
701 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
702 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
703 }
704 };
705
706 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
707 path, key, element,
708 ))
709 }
710}
711
712pub trait DriveCost {
714 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
716}
717
718impl DriveCost for OperationCost {
719 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
721 let OperationCost {
722 seek_count,
723 storage_cost,
724 storage_loaded_bytes,
725 hash_node_calls,
726 sinsemilla_hash_calls,
727 } = self;
728 let epoch_cost_for_processing_credit_per_byte =
729 fee_version.storage.storage_processing_credit_per_byte;
730 let seek_cost = (*seek_count as u64)
731 .checked_mul(fee_version.storage.storage_seek_cost)
732 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
733 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
734 .checked_mul(epoch_cost_for_processing_credit_per_byte)
735 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
736 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
737 .checked_mul(epoch_cost_for_processing_credit_per_byte)
738 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
739 let storage_removed_bytes_ephemeral_cost =
740 (storage_cost.removed_bytes.total_removed_bytes() as u64)
741 .checked_mul(epoch_cost_for_processing_credit_per_byte)
742 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
743 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
745 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
746 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
747
748 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
750 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
752 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
753 seek_cost
754 .checked_add(storage_added_bytes_ephemeral_cost)
755 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
756 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
757 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
758 .and_then(|c| c.checked_add(hash_node_cost))
759 .and_then(|c| c.checked_add(sinsemilla_cost))
760 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
761 }
762}
763
764#[cfg(test)]
765#[allow(clippy::identity_op)]
766mod tests {
767 use super::*;
768 use grovedb_costs::storage_cost::removal::StorageRemovedBytes;
769 use grovedb_costs::storage_cost::StorageCost;
770 use platform_version::version::fee::storage::FeeStorageVersion;
771 use platform_version::version::fee::FeeVersion;
772
773 fn fee_version() -> &'static FeeVersion {
775 FeeVersion::first()
776 }
777
778 #[test]
783 fn base_op_stop_costs_zero() {
784 assert_eq!(BaseOp::Stop.cost(), 0);
785 }
786
787 #[test]
788 fn base_op_add_costs_12() {
789 assert_eq!(BaseOp::Add.cost(), 12);
790 }
791
792 #[test]
793 fn base_op_mul_costs_20() {
794 assert_eq!(BaseOp::Mul.cost(), 20);
795 }
796
797 #[test]
798 fn base_op_signextend_costs_20() {
799 assert_eq!(BaseOp::Signextend.cost(), 20);
800 }
801
802 #[test]
803 fn base_op_addmod_costs_32() {
804 assert_eq!(BaseOp::Addmod.cost(), 32);
805 }
806
807 #[test]
808 fn base_op_mulmod_costs_32() {
809 assert_eq!(BaseOp::Mulmod.cost(), 32);
810 }
811
812 #[test]
813 fn base_op_byte_costs_12() {
814 assert_eq!(BaseOp::Byte.cost(), 12);
815 }
816
817 #[test]
818 fn base_op_sub_costs_12() {
819 assert_eq!(BaseOp::Sub.cost(), 12);
820 }
821
822 #[test]
823 fn base_op_div_costs_20() {
824 assert_eq!(BaseOp::Div.cost(), 20);
825 }
826
827 #[test]
828 fn base_op_comparison_ops_all_cost_12() {
829 for op in [
830 BaseOp::Lt,
831 BaseOp::Gt,
832 BaseOp::Slt,
833 BaseOp::Sgt,
834 BaseOp::Eq,
835 BaseOp::Iszero,
836 ] {
837 assert_eq!(op.cost(), 12, "comparison op {:?} should cost 12", op);
838 }
839 }
840
841 #[test]
842 fn base_op_bitwise_ops_all_cost_12() {
843 for op in [BaseOp::And, BaseOp::Or, BaseOp::Xor, BaseOp::Not] {
844 assert_eq!(op.cost(), 12, "bitwise op {:?} should cost 12", op);
845 }
846 }
847
848 #[test]
853 fn hash_function_block_size_all_64() {
854 assert_eq!(HashFunction::Sha256.block_size(), 64);
856 assert_eq!(HashFunction::Sha256_2.block_size(), 64);
857 assert_eq!(HashFunction::Blake3.block_size(), 64);
858 assert_eq!(HashFunction::Sha256RipeMD160.block_size(), 64);
859 }
860
861 #[test]
862 fn hash_function_rounds() {
863 assert_eq!(HashFunction::Sha256.rounds(), 1);
864 assert_eq!(HashFunction::Sha256_2.rounds(), 2);
865 assert_eq!(HashFunction::Blake3.rounds(), 1);
866 assert_eq!(HashFunction::Sha256RipeMD160.rounds(), 1);
867 }
868
869 #[test]
870 fn hash_function_block_cost_sha256_variants_use_sha256_per_block() {
871 let fv = fee_version();
872 let expected = fv.hashing.sha256_per_block;
873 assert_eq!(HashFunction::Sha256.block_cost(fv), expected);
874 assert_eq!(HashFunction::Sha256_2.block_cost(fv), expected);
875 assert_eq!(HashFunction::Sha256RipeMD160.block_cost(fv), expected);
876 }
877
878 #[test]
879 fn hash_function_block_cost_blake3_uses_blake3_per_block() {
880 let fv = fee_version();
881 assert_eq!(
882 HashFunction::Blake3.block_cost(fv),
883 fv.hashing.blake3_per_block
884 );
885 }
886
887 #[test]
888 fn hash_function_base_cost_sha256() {
889 let fv = fee_version();
890 assert_eq!(
891 HashFunction::Sha256.base_cost(fv),
892 fv.hashing.single_sha256_base
893 );
894 }
895
896 #[test]
897 fn hash_function_base_cost_sha256_2_uses_single_sha256_base() {
898 let fv = fee_version();
899 assert_eq!(
901 HashFunction::Sha256_2.base_cost(fv),
902 fv.hashing.single_sha256_base
903 );
904 }
905
906 #[test]
907 fn hash_function_base_cost_blake3() {
908 let fv = fee_version();
909 assert_eq!(HashFunction::Blake3.base_cost(fv), fv.hashing.blake3_base);
910 }
911
912 #[test]
913 fn hash_function_base_cost_sha256_ripe_md160() {
914 let fv = fee_version();
915 assert_eq!(
916 HashFunction::Sha256RipeMD160.base_cost(fv),
917 fv.hashing.sha256_ripe_md160_base
918 );
919 }
920
921 #[test]
926 fn function_op_new_with_byte_count_small_sha256() {
927 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 32);
929 assert_eq!(op.rounds, 1);
930 assert_eq!(op.hash, HashFunction::Sha256);
931 }
932
933 #[test]
934 fn function_op_new_with_byte_count_exact_block_boundary_sha256() {
935 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 64);
937 assert_eq!(op.rounds, 2);
938 }
939
940 #[test]
941 fn function_op_new_with_byte_count_large_sha256() {
942 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 200);
944 assert_eq!(op.rounds, 4);
945 }
946
947 #[test]
948 fn function_op_new_with_byte_count_sha256_2_has_extra_round() {
949 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 32);
951 assert_eq!(op.rounds, 2);
952 }
953
954 #[test]
955 fn function_op_new_with_byte_count_sha256_2_large() {
956 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 200);
958 assert_eq!(op.rounds, 5);
959 }
960
961 #[test]
962 fn function_op_new_with_byte_count_blake3_small() {
963 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 10);
965 assert_eq!(op.rounds, 1);
966 assert_eq!(op.hash, HashFunction::Blake3);
967 }
968
969 #[test]
970 fn function_op_new_with_byte_count_blake3_large() {
971 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 500);
973 assert_eq!(op.rounds, 8);
974 }
975
976 #[test]
977 fn function_op_new_with_byte_count_zero_bytes() {
978 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 0);
980 assert_eq!(op.rounds, 1);
981 }
982
983 #[test]
984 fn function_op_new_with_byte_count_sha256_ripemd160() {
985 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256RipeMD160, 20);
987 assert_eq!(op.rounds, 1);
988 assert_eq!(op.hash, HashFunction::Sha256RipeMD160);
989 }
990
991 #[test]
996 fn function_op_cost_sha256_one_round() {
997 let fv = fee_version();
998 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 1);
999 let expected = fv.hashing.single_sha256_base + 1 * fv.hashing.sha256_per_block;
1001 assert_eq!(op.cost(fv), expected);
1002 }
1003
1004 #[test]
1005 fn function_op_cost_sha256_2_two_rounds() {
1006 let fv = fee_version();
1007 let op = FunctionOp::new_with_round_count(HashFunction::Sha256_2, 2);
1008 let expected = fv.hashing.single_sha256_base + 2 * fv.hashing.sha256_per_block;
1010 assert_eq!(op.cost(fv), expected);
1011 }
1012
1013 #[test]
1014 fn function_op_cost_blake3_one_round() {
1015 let fv = fee_version();
1016 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 1);
1017 let expected = fv.hashing.blake3_base + 1 * fv.hashing.blake3_per_block;
1019 assert_eq!(op.cost(fv), expected);
1020 }
1021
1022 #[test]
1023 fn function_op_cost_zero_rounds() {
1024 let fv = fee_version();
1025 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 0);
1026 assert_eq!(op.cost(fv), fv.hashing.blake3_base);
1028 }
1029
1030 #[test]
1031 fn function_op_cost_from_byte_count_matches_manual_calc() {
1032 let fv = fee_version();
1033 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 128);
1035 assert_eq!(op.rounds, 3);
1036 let expected = fv.hashing.single_sha256_base + 3 * fv.hashing.sha256_per_block;
1037 assert_eq!(op.cost(fv), expected);
1038 }
1039
1040 #[test]
1041 fn function_op_cost_sha256_ripemd160() {
1042 let fv = fee_version();
1043 let op = FunctionOp::new_with_round_count(HashFunction::Sha256RipeMD160, 1);
1044 let expected = fv.hashing.sha256_ripe_md160_base + 1 * fv.hashing.sha256_per_block;
1045 assert_eq!(op.cost(fv), expected);
1046 }
1047
1048 #[test]
1049 fn function_op_cost_saturating_mul_does_not_panic_on_large_rounds() {
1050 let fv = fee_version();
1051 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, u32::MAX);
1052 let expected_block_cost = (u32::MAX as u64).saturating_mul(fv.hashing.sha256_per_block);
1055 let expected = fv
1056 .hashing
1057 .single_sha256_base
1058 .saturating_add(expected_block_cost);
1059 assert_eq!(op.cost(fv), expected);
1060 }
1061
1062 #[test]
1063 fn function_op_cost_saturates_to_max_with_extreme_fee_version() {
1064 let mut fv = fee_version().clone();
1067 fv.hashing.sha256_per_block = u64::MAX;
1068 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 2);
1069 assert_eq!(op.cost(&fv), u64::MAX);
1071 }
1072
1073 #[test]
1078 fn operation_cost_calculated_cost_operation_returns_cost() {
1079 let cost = OperationCost {
1080 seek_count: 3,
1081 storage_cost: StorageCost {
1082 added_bytes: 100,
1083 replaced_bytes: 50,
1084 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1085 },
1086 storage_loaded_bytes: 200,
1087 hash_node_calls: 5,
1088 sinsemilla_hash_calls: 0,
1089 };
1090 let op = CalculatedCostOperation(cost.clone());
1091 let result = op.operation_cost().expect("should return Ok");
1092 assert_eq!(result, cost);
1093 }
1094
1095 #[test]
1096 fn operation_cost_grove_operation_returns_error() {
1097 let grove_op = LowLevelDriveOperation::insert_for_known_path_key_element(
1098 vec![vec![1, 2, 3]],
1099 vec![4, 5, 6],
1100 Element::empty_tree(),
1101 );
1102 let result = grove_op.operation_cost();
1103 assert!(result.is_err());
1104 let err_msg = format!("{:?}", result.unwrap_err());
1105 assert!(
1106 err_msg.contains("grove operations must be executed"),
1107 "unexpected error: {}",
1108 err_msg
1109 );
1110 }
1111
1112 #[test]
1113 fn operation_cost_pre_calculated_fee_result_returns_error() {
1114 let fee = FeeResult {
1115 storage_fee: 100,
1116 processing_fee: 200,
1117 ..Default::default()
1118 };
1119 let op = PreCalculatedFeeResult(fee);
1120 let result = op.operation_cost();
1121 assert!(result.is_err());
1122 let err_msg = format!("{:?}", result.unwrap_err());
1123 assert!(
1124 err_msg.contains("pre calculated fees should not be requested"),
1125 "unexpected error: {}",
1126 err_msg
1127 );
1128 }
1129
1130 #[test]
1131 fn operation_cost_function_operation_returns_error() {
1132 let func_op = FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1));
1133 let result = func_op.operation_cost();
1134 assert!(result.is_err());
1135 let err_msg = format!("{:?}", result.unwrap_err());
1136 assert!(
1137 err_msg.contains("function operations should not be requested"),
1138 "unexpected error: {}",
1139 err_msg
1140 );
1141 }
1142
1143 #[test]
1148 fn combine_cost_operations_sums_calculated_costs_only() {
1149 let cost1 = OperationCost {
1150 seek_count: 2,
1151 storage_cost: StorageCost {
1152 added_bytes: 10,
1153 replaced_bytes: 0,
1154 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1155 },
1156 storage_loaded_bytes: 50,
1157 hash_node_calls: 1,
1158 sinsemilla_hash_calls: 0,
1159 };
1160 let cost2 = OperationCost {
1161 seek_count: 3,
1162 storage_cost: StorageCost {
1163 added_bytes: 20,
1164 replaced_bytes: 5,
1165 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1166 },
1167 storage_loaded_bytes: 100,
1168 hash_node_calls: 2,
1169 sinsemilla_hash_calls: 1,
1170 };
1171
1172 let operations = vec![
1173 CalculatedCostOperation(cost1.clone()),
1174 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1176 CalculatedCostOperation(cost2.clone()),
1177 PreCalculatedFeeResult(FeeResult::default()),
1179 ];
1180
1181 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
1182 assert_eq!(combined.seek_count, 2 + 3);
1183 assert_eq!(combined.storage_cost.added_bytes, 10 + 20);
1184 assert_eq!(combined.storage_cost.replaced_bytes, 0 + 5);
1185 assert_eq!(combined.storage_loaded_bytes, 50 + 100);
1186 assert_eq!(combined.hash_node_calls, 1 + 2);
1187 assert_eq!(combined.sinsemilla_hash_calls, 0 + 1);
1188 }
1189
1190 #[test]
1191 fn combine_cost_operations_empty_list_returns_default() {
1192 let combined = LowLevelDriveOperation::combine_cost_operations(&[]);
1193 assert_eq!(combined, OperationCost::default());
1194 }
1195
1196 #[test]
1197 fn combine_cost_operations_no_calculated_costs_returns_default() {
1198 let operations = vec![
1199 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 2)),
1200 PreCalculatedFeeResult(FeeResult {
1201 processing_fee: 999,
1202 ..Default::default()
1203 }),
1204 ];
1205 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
1206 assert_eq!(combined, OperationCost::default());
1207 }
1208
1209 fn make_grove_op(key_byte: u8) -> LowLevelDriveOperation {
1215 LowLevelDriveOperation::insert_for_known_path_key_element(
1216 vec![vec![0]],
1217 vec![key_byte],
1218 Element::new_item(vec![key_byte]),
1219 )
1220 }
1221
1222 fn make_mixed_ops() -> Vec<LowLevelDriveOperation> {
1223 vec![
1224 make_grove_op(1),
1225 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1226 make_grove_op(2),
1227 CalculatedCostOperation(OperationCost::default()),
1228 make_grove_op(3),
1229 ]
1230 }
1231
1232 #[test]
1233 fn grovedb_operations_batch_filters_grove_ops_from_ref() {
1234 let ops = make_mixed_ops();
1235 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
1236 assert_eq!(batch.len(), 3);
1237 }
1238
1239 #[test]
1240 fn grovedb_operations_batch_empty_input() {
1241 let batch = LowLevelDriveOperation::grovedb_operations_batch(&[]);
1242 assert!(batch.is_empty());
1243 }
1244
1245 #[test]
1246 fn grovedb_operations_batch_no_grove_ops() {
1247 let ops = vec![
1248 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1)),
1249 CalculatedCostOperation(OperationCost::default()),
1250 ];
1251 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
1252 assert!(batch.is_empty());
1253 }
1254
1255 #[test]
1256 fn grovedb_operations_batch_consume_filters_grove_ops() {
1257 let ops = make_mixed_ops();
1258 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(ops);
1259 assert_eq!(batch.len(), 3);
1260 }
1261
1262 #[test]
1263 fn grovedb_operations_batch_consume_empty_input() {
1264 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(vec![]);
1265 assert!(batch.is_empty());
1266 }
1267
1268 #[test]
1269 fn grovedb_operations_batch_consume_with_leftovers_partitions_correctly() {
1270 let ops = make_mixed_ops();
1271 let (batch, leftovers) =
1272 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1273 assert_eq!(batch.len(), 3);
1274 assert_eq!(leftovers.len(), 2);
1275
1276 for leftover in &leftovers {
1278 assert!(
1279 !matches!(leftover, GroveOperation(_)),
1280 "leftovers should not contain GroveOperation variants"
1281 );
1282 }
1283 }
1284
1285 #[test]
1286 fn grovedb_operations_batch_consume_with_leftovers_all_grove() {
1287 let ops = vec![make_grove_op(10), make_grove_op(20)];
1288 let (batch, leftovers) =
1289 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1290 assert_eq!(batch.len(), 2);
1291 assert!(leftovers.is_empty());
1292 }
1293
1294 #[test]
1295 fn grovedb_operations_batch_consume_with_leftovers_no_grove() {
1296 let ops = vec![
1297 CalculatedCostOperation(OperationCost::default()),
1298 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1299 ];
1300 let (batch, leftovers) =
1301 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1302 assert!(batch.is_empty());
1303 assert_eq!(leftovers.len(), 2);
1304 }
1305
1306 #[test]
1307 fn grovedb_operations_batch_consume_with_leftovers_empty() {
1308 let (batch, leftovers) =
1309 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(vec![]);
1310 assert!(batch.is_empty());
1311 assert!(leftovers.is_empty());
1312 }
1313
1314 #[test]
1319 fn ephemeral_cost_zero_operation() {
1320 let fv = fee_version();
1321 let cost = OperationCost::default();
1322 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1323 assert_eq!(result, 0);
1324 }
1325
1326 #[test]
1327 fn ephemeral_cost_seek_only() {
1328 let fv = fee_version();
1329 let cost = OperationCost {
1330 seek_count: 5,
1331 storage_cost: StorageCost::default(),
1332 storage_loaded_bytes: 0,
1333 hash_node_calls: 0,
1334 sinsemilla_hash_calls: 0,
1335 };
1336 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1337 let expected = 5u64 * fv.storage.storage_seek_cost;
1338 assert_eq!(result, expected);
1339 }
1340
1341 #[test]
1342 fn ephemeral_cost_storage_added_bytes() {
1343 let fv = fee_version();
1344 let cost = OperationCost {
1345 seek_count: 0,
1346 storage_cost: StorageCost {
1347 added_bytes: 100,
1348 replaced_bytes: 0,
1349 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1350 },
1351 storage_loaded_bytes: 0,
1352 hash_node_calls: 0,
1353 sinsemilla_hash_calls: 0,
1354 };
1355 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1356 let expected = 100u64 * fv.storage.storage_processing_credit_per_byte;
1357 assert_eq!(result, expected);
1358 }
1359
1360 #[test]
1361 fn ephemeral_cost_storage_replaced_bytes() {
1362 let fv = fee_version();
1363 let cost = OperationCost {
1364 seek_count: 0,
1365 storage_cost: StorageCost {
1366 added_bytes: 0,
1367 replaced_bytes: 50,
1368 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1369 },
1370 storage_loaded_bytes: 0,
1371 hash_node_calls: 0,
1372 sinsemilla_hash_calls: 0,
1373 };
1374 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1375 let expected = 50u64 * fv.storage.storage_processing_credit_per_byte;
1376 assert_eq!(result, expected);
1377 }
1378
1379 #[test]
1380 fn ephemeral_cost_storage_removed_bytes_basic() {
1381 let fv = fee_version();
1382 let cost = OperationCost {
1383 seek_count: 0,
1384 storage_cost: StorageCost {
1385 added_bytes: 0,
1386 replaced_bytes: 0,
1387 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(75),
1388 },
1389 storage_loaded_bytes: 0,
1390 hash_node_calls: 0,
1391 sinsemilla_hash_calls: 0,
1392 };
1393 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1394 let expected = 75u64 * fv.storage.storage_processing_credit_per_byte;
1395 assert_eq!(result, expected);
1396 }
1397
1398 #[test]
1399 fn ephemeral_cost_loaded_bytes() {
1400 let fv = fee_version();
1401 let cost = OperationCost {
1402 seek_count: 0,
1403 storage_cost: StorageCost::default(),
1404 storage_loaded_bytes: 300,
1405 hash_node_calls: 0,
1406 sinsemilla_hash_calls: 0,
1407 };
1408 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1409 let expected = 300u64 * fv.storage.storage_load_credit_per_byte;
1410 assert_eq!(result, expected);
1411 }
1412
1413 #[test]
1414 fn ephemeral_cost_hash_node_calls() {
1415 let fv = fee_version();
1416 let cost = OperationCost {
1417 seek_count: 0,
1418 storage_cost: StorageCost::default(),
1419 storage_loaded_bytes: 0,
1420 hash_node_calls: 10,
1421 sinsemilla_hash_calls: 0,
1422 };
1423 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1424 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
1425 let expected = blake3_total * 10;
1426 assert_eq!(result, expected);
1427 }
1428
1429 #[test]
1430 fn ephemeral_cost_sinsemilla_hash_calls() {
1431 let fv = fee_version();
1432 let cost = OperationCost {
1433 seek_count: 0,
1434 storage_cost: StorageCost::default(),
1435 storage_loaded_bytes: 0,
1436 hash_node_calls: 0,
1437 sinsemilla_hash_calls: 3,
1438 };
1439 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1440 let expected = fv.hashing.sinsemilla_base * 3;
1441 assert_eq!(result, expected);
1442 }
1443
1444 #[test]
1445 fn ephemeral_cost_all_components_combined() {
1446 let fv = fee_version();
1447 let cost = OperationCost {
1448 seek_count: 2,
1449 storage_cost: StorageCost {
1450 added_bytes: 10,
1451 replaced_bytes: 20,
1452 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(30),
1453 },
1454 storage_loaded_bytes: 40,
1455 hash_node_calls: 5,
1456 sinsemilla_hash_calls: 1,
1457 };
1458 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1459
1460 let seek_cost = 2u64 * fv.storage.storage_seek_cost;
1461 let processing_per_byte = fv.storage.storage_processing_credit_per_byte;
1462 let added_cost = 10u64 * processing_per_byte;
1463 let replaced_cost = 20u64 * processing_per_byte;
1464 let removed_cost = 30u64 * processing_per_byte;
1465 let loaded_cost = 40u64 * fv.storage.storage_load_credit_per_byte;
1466 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
1467 let hash_cost = blake3_total * 5;
1468 let sinsemilla_cost = fv.hashing.sinsemilla_base * 1;
1469
1470 let expected = seek_cost
1471 + added_cost
1472 + replaced_cost
1473 + loaded_cost
1474 + removed_cost
1475 + hash_cost
1476 + sinsemilla_cost;
1477 assert_eq!(result, expected);
1478 }
1479
1480 #[test]
1481 fn ephemeral_cost_overflow_seek_cost() {
1482 let fv = &FeeVersion {
1483 storage: FeeStorageVersion {
1484 storage_seek_cost: u64::MAX,
1485 ..fee_version().storage.clone()
1486 },
1487 ..fee_version().clone()
1488 };
1489 let cost = OperationCost {
1490 seek_count: 2, storage_cost: StorageCost::default(),
1492 storage_loaded_bytes: 0,
1493 hash_node_calls: 0,
1494 sinsemilla_hash_calls: 0,
1495 };
1496 let result = cost.ephemeral_cost(fv);
1497 assert!(result.is_err(), "expected overflow error for seek cost");
1498 }
1499
1500 #[test]
1501 fn ephemeral_cost_overflow_storage_written_bytes() {
1502 let fv = &FeeVersion {
1503 storage: FeeStorageVersion {
1504 storage_processing_credit_per_byte: u64::MAX,
1505 ..fee_version().storage.clone()
1506 },
1507 ..fee_version().clone()
1508 };
1509 let cost = OperationCost {
1510 seek_count: 0,
1511 storage_cost: StorageCost {
1512 added_bytes: 2, replaced_bytes: 0,
1514 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1515 },
1516 storage_loaded_bytes: 0,
1517 hash_node_calls: 0,
1518 sinsemilla_hash_calls: 0,
1519 };
1520 let result = cost.ephemeral_cost(fv);
1521 assert!(
1522 result.is_err(),
1523 "expected overflow error for storage written bytes"
1524 );
1525 }
1526
1527 #[test]
1528 fn ephemeral_cost_overflow_loaded_bytes() {
1529 let fv = &FeeVersion {
1530 storage: FeeStorageVersion {
1531 storage_load_credit_per_byte: u64::MAX,
1532 ..fee_version().storage.clone()
1533 },
1534 ..fee_version().clone()
1535 };
1536 let cost = OperationCost {
1537 seek_count: 0,
1538 storage_cost: StorageCost::default(),
1539 storage_loaded_bytes: 2, hash_node_calls: 0,
1541 sinsemilla_hash_calls: 0,
1542 };
1543 let result = cost.ephemeral_cost(fv);
1544 assert!(
1545 result.is_err(),
1546 "expected overflow error for loaded bytes cost"
1547 );
1548 }
1549
1550 #[test]
1551 fn ephemeral_cost_overflow_in_addition_chain() {
1552 let fv = fee_version();
1554 let cost = OperationCost {
1555 seek_count: u32::MAX,
1556 storage_cost: StorageCost {
1557 added_bytes: u32::MAX,
1558 replaced_bytes: u32::MAX,
1559 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(u32::MAX),
1560 },
1561 storage_loaded_bytes: u64::MAX,
1562 hash_node_calls: u32::MAX,
1563 sinsemilla_hash_calls: u32::MAX,
1564 };
1565 let result = cost.ephemeral_cost(fv);
1566 assert!(
1567 result.is_err(),
1568 "expected overflow error when summing large components"
1569 );
1570 }
1571}