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::IndexAxis;
13use grovedb::element::MaxReferenceHop;
14use grovedb::{batch::QualifiedGroveDbOp, Element, ElementFlags, TreeType};
15use grovedb_costs::OperationCost;
16use itertools::Itertools;
17
18use crate::error::drive::DriveError;
19use crate::error::Error;
20use crate::fees::get_overflow_error;
21use crate::fees::op::LowLevelDriveOperation::{
22 CalculatedCostOperation, FunctionOperation, GroveOperation, PreCalculatedFeeResult,
23};
24use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods;
25use crate::util::storage_flags::StorageFlags;
26use dpp::block::epoch::Epoch;
27use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
28use dpp::fee::fee_result::refunds::FeeRefunds;
29use dpp::fee::fee_result::FeeResult;
30use dpp::fee::Credits;
31use platform_version::version::fee::FeeVersion;
32
33#[derive(Debug, Enum)]
35pub enum BaseOp {
36 Stop,
38 Add,
40 Mul,
42 Sub,
44 Div,
46 Sdiv,
48 Mod,
50 Smod,
52 Addmod,
54 Mulmod,
56 Signextend,
58 Lt,
60 Gt,
62 Slt,
64 Sgt,
66 Eq,
68 Iszero,
70 And,
72 Or,
74 Xor,
76 Not,
78 Byte,
80}
81
82impl BaseOp {
83 pub fn cost(&self) -> u64 {
85 match self {
86 BaseOp::Stop => 0,
87 BaseOp::Add => 12,
88 BaseOp::Mul => 20,
89 BaseOp::Sub => 12,
90 BaseOp::Div => 20,
91 BaseOp::Sdiv => 20,
92 BaseOp::Mod => 20,
93 BaseOp::Smod => 20,
94 BaseOp::Addmod => 32,
95 BaseOp::Mulmod => 32,
96 BaseOp::Signextend => 20,
97 BaseOp::Lt => 12,
98 BaseOp::Gt => 12,
99 BaseOp::Slt => 12,
100 BaseOp::Sgt => 12,
101 BaseOp::Eq => 12,
102 BaseOp::Iszero => 12,
103 BaseOp::And => 12,
104 BaseOp::Or => 12,
105 BaseOp::Xor => 12,
106 BaseOp::Not => 12,
107 BaseOp::Byte => 12,
108 }
109 }
110}
111
112#[derive(Debug, Enum, PartialEq, Eq)]
114pub enum HashFunction {
115 Sha256RipeMD160,
117 Sha256,
119 Sha256_2,
121 Blake3,
123}
124
125impl HashFunction {
126 fn block_size(&self) -> u16 {
127 match self {
128 HashFunction::Sha256 => 64,
129 HashFunction::Sha256_2 => 64,
130 HashFunction::Blake3 => 64,
131 HashFunction::Sha256RipeMD160 => 64,
132 }
133 }
134
135 fn rounds(&self) -> u16 {
136 match self {
137 HashFunction::Sha256 => 1,
138 HashFunction::Sha256_2 => 2,
139 HashFunction::Blake3 => 1,
140 HashFunction::Sha256RipeMD160 => 1,
141 }
142 }
143
144 fn block_cost(&self, fee_version: &FeeVersion) -> u64 {
145 match self {
146 HashFunction::Sha256 => fee_version.hashing.sha256_per_block,
147 HashFunction::Sha256_2 => fee_version.hashing.sha256_per_block,
148 HashFunction::Blake3 => fee_version.hashing.blake3_per_block,
149 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_per_block,
150 }
151 }
152
153 fn base_cost(&self, fee_version: &FeeVersion) -> u64 {
154 match self {
155 HashFunction::Sha256 => fee_version.hashing.single_sha256_base,
156 HashFunction::Sha256_2 => fee_version.hashing.single_sha256_base,
159 HashFunction::Blake3 => fee_version.hashing.blake3_base,
160 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_ripe_md160_base,
161 }
162 }
163}
164
165#[derive(Debug, PartialEq, Eq)]
167pub struct FunctionOp {
168 pub(crate) hash: HashFunction,
170 pub(crate) rounds: u32,
172}
173
174impl FunctionOp {
175 fn cost(&self, fee_version: &FeeVersion) -> Credits {
177 let block_cost = (self.rounds as u64).saturating_mul(self.hash.block_cost(fee_version));
178 self.hash.base_cost(fee_version).saturating_add(block_cost)
179 }
180
181 pub fn new_with_round_count(hash: HashFunction, rounds: u32) -> Self {
184 FunctionOp { hash, rounds }
185 }
186
187 pub fn new_with_byte_count(hash: HashFunction, byte_count: u16) -> Self {
190 let blocks = byte_count / hash.block_size() + 1;
191 let rounds = blocks + hash.rounds() - 1;
192 FunctionOp {
193 hash,
194 rounds: rounds as u32,
195 }
196 }
197}
198
199#[allow(clippy::large_enum_variant)]
204#[derive(Debug, Eq, PartialEq)]
205pub enum LowLevelDriveOperation {
206 GroveOperation(QualifiedGroveDbOp),
208 FunctionOperation(FunctionOp),
210 CalculatedCostOperation(OperationCost),
212 PreCalculatedFeeResult(FeeResult),
214}
215
216const INDEXED_INNER_UNWRAPPABLE: &str =
236 "an indexed tree cannot be wrapped in NonCounted / NotSummed / NotCountedOrSummed: the \
237 wrapper suppresses the subtree's contribution to its parent's aggregate, but an indexed \
238 primary commits its aggregate (and the derived secondary root keys) through that very \
239 parent element. A ranked index's terminal property-name tree therefore cannot live inside \
240 an aggregating value tree — i.e. a ranked compound index [a, b] cannot coexist with a \
241 countable/summable index terminating at [a]; contracts declaring that pair are rejected \
242 at parse time.";
243
244impl LowLevelDriveOperation {
245 pub fn consume_to_fees_v0(
248 drive_operations: Vec<LowLevelDriveOperation>,
249 epoch: &Epoch,
250 epochs_per_era: u16,
251 fee_version: &FeeVersion,
252 previous_fee_versions: Option<&CachedEpochIndexFeeVersions>,
253 ) -> Result<Vec<FeeResult>, Error> {
254 drive_operations
255 .into_iter()
256 .map(|operation| match operation {
257 PreCalculatedFeeResult(f) => Ok(f),
258 FunctionOperation(op) => Ok(FeeResult {
259 processing_fee: op.cost(fee_version),
260 ..Default::default()
261 }),
262 _ => {
263 let cost = operation.operation_cost()?;
264 let storage_fee = cost.storage_cost.added_bytes as u64 * fee_version.storage.storage_disk_usage_credit_per_byte;
267 let processing_fee = cost.ephemeral_cost(fee_version)?;
268 let (fee_refunds, removed_bytes_from_system) =
269 match cost.storage_cost.removed_bytes {
270 NoStorageRemoval => (FeeRefunds::default(), 0),
271 BasicStorageRemoval(amount) => {
272 (FeeRefunds::default(), amount)
274 }
275 SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => {
276
277 let system_amount = removal_per_epoch_by_identifier
278 .remove(&Identifier::default())
279 .map_or(0, |a| a.values().sum());
280 if fee_version.fee_version_number == 1 {
281 (
282 FeeRefunds::from_storage_removal(
283 removal_per_epoch_by_identifier,
284 epoch.index,
285 epochs_per_era,
286 &BTreeMap::default(),
287 )?,
288 system_amount,
289 )
290 } else {
291 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")))?;
292 (
293 FeeRefunds::from_storage_removal(
294 removal_per_epoch_by_identifier,
295 epoch.index,
296 epochs_per_era,
297 previous_fee_versions,
298 )?,
299 system_amount,
300 )
301 }
302 }
303 };
304 Ok(FeeResult {
305 storage_fee,
306 processing_fee,
307 fee_refunds,
308 removed_bytes_from_system,
309 })
310 }
311 })
312 .collect()
313 }
314
315 pub fn operation_cost(self) -> Result<OperationCost, Error> {
317 match self {
318 GroveOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
319 "grove operations must be executed, not directly transformed to costs",
320 ))),
321 CalculatedCostOperation(c) => Ok(c),
322 PreCalculatedFeeResult(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
323 "pre calculated fees should not be requested by operation costs",
324 ))),
325 FunctionOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
326 "function operations should not be requested by operation costs",
327 ))),
328 }
329 }
330
331 pub fn combine_cost_operations(operations: &[LowLevelDriveOperation]) -> OperationCost {
333 let mut cost = OperationCost::default();
334 operations.iter().for_each(|op| {
335 if let CalculatedCostOperation(operation_cost) = op {
336 cost += operation_cost.clone()
337 }
338 });
339 cost
340 }
341
342 pub fn grovedb_operations_batch(
344 insert_operations: &[LowLevelDriveOperation],
345 ) -> GroveDbOpBatch {
346 let operations = insert_operations
347 .iter()
348 .filter_map(|op| match op {
349 GroveOperation(grovedb_op) => Some(grovedb_op.clone()),
350 _ => None,
351 })
352 .collect();
353 GroveDbOpBatch::from_operations(operations)
354 }
355
356 pub fn grovedb_operations_batch_consume(
358 insert_operations: Vec<LowLevelDriveOperation>,
359 ) -> GroveDbOpBatch {
360 let operations = insert_operations
361 .into_iter()
362 .filter_map(|op| match op {
363 GroveOperation(grovedb_op) => Some(grovedb_op),
364 _ => None,
365 })
366 .collect();
367 GroveDbOpBatch::from_operations(operations)
368 }
369
370 pub fn grovedb_operations_batch_consume_with_leftovers(
372 insert_operations: Vec<LowLevelDriveOperation>,
373 ) -> (GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
374 let (grove_operations, other_operations): (Vec<_>, Vec<_>) =
375 insert_operations.into_iter().partition_map(|op| match op {
376 GroveOperation(grovedb_op) => itertools::Either::Left(grovedb_op),
377 _ => itertools::Either::Right(op),
378 });
379
380 (
381 GroveDbOpBatch::from_operations(grove_operations),
382 other_operations,
383 )
384 }
385
386 pub fn grovedb_operations_consume(
388 insert_operations: Vec<LowLevelDriveOperation>,
389 ) -> Vec<QualifiedGroveDbOp> {
390 insert_operations
391 .into_iter()
392 .filter_map(|op| match op {
393 GroveOperation(grovedb_op) => Some(grovedb_op),
394 _ => None,
395 })
396 .collect()
397 }
398
399 pub fn for_known_path_key_empty_tree(
401 path: Vec<Vec<u8>>,
402 key: Vec<u8>,
403 storage_flags: Option<&StorageFlags>,
404 ) -> Self {
405 let tree = match storage_flags {
406 Some(storage_flags) => {
407 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
408 }
409 None => Element::empty_tree(),
410 };
411
412 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
413 }
414
415 pub fn for_known_path_key_empty_sum_tree(
417 path: Vec<Vec<u8>>,
418 key: Vec<u8>,
419 storage_flags: Option<&StorageFlags>,
420 ) -> Self {
421 let tree = match storage_flags {
422 Some(storage_flags) => {
423 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
424 }
425 None => Element::empty_sum_tree(),
426 };
427
428 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
429 }
430
431 pub fn for_known_path_key_empty_big_sum_tree(
433 path: Vec<Vec<u8>>,
434 key: Vec<u8>,
435 storage_flags: Option<&StorageFlags>,
436 ) -> Self {
437 let tree = match storage_flags {
438 Some(storage_flags) => {
439 Element::new_big_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
440 }
441 None => Element::empty_big_sum_tree(),
442 };
443
444 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
445 }
446
447 pub fn for_known_path_key_empty_count_tree(
449 path: Vec<Vec<u8>>,
450 key: Vec<u8>,
451 storage_flags: Option<&StorageFlags>,
452 ) -> Self {
453 let tree = match storage_flags {
454 Some(storage_flags) => {
455 Element::new_count_tree_with_flags(None, storage_flags.to_some_element_flags())
456 }
457 None => Element::empty_count_tree(),
458 };
459
460 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
461 }
462
463 pub fn for_known_path_key_empty_count_sum_tree(
465 path: Vec<Vec<u8>>,
466 key: Vec<u8>,
467 storage_flags: Option<&StorageFlags>,
468 ) -> Self {
469 let tree = match storage_flags {
470 Some(storage_flags) => {
471 Element::new_count_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
472 }
473 None => Element::new_count_sum_tree(None),
474 };
475
476 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
477 }
478
479 pub fn for_known_path_key_empty_non_counted_normal_tree(
487 path: Vec<Vec<u8>>,
488 key: Vec<u8>,
489 storage_flags: Option<&StorageFlags>,
490 ) -> Self {
491 Self::for_known_path_key_empty_non_counted_tree(
492 path,
493 key,
494 TreeType::NormalTree,
495 storage_flags,
496 )
497 .expect("NormalTree NonCounted wrapping never fails")
498 }
499
500 pub fn for_known_path_key_empty_non_counted_tree(
517 path: Vec<Vec<u8>>,
518 key: Vec<u8>,
519 tree_type: TreeType,
520 storage_flags: Option<&StorageFlags>,
521 ) -> Result<Self, Error> {
522 let element_flags = storage_flags.map(|s| s.to_element_flags());
529 let inner = match tree_type {
530 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
531 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
532 TreeType::ProvableCountTree => {
533 Element::empty_provable_count_tree_with_flags(element_flags)
534 }
535 TreeType::ProvableSumIndexedTree
536 | TreeType::ProvableCountIndexedTree
537 | TreeType::ProvableCountProvableSumIndexedTree => {
538 return Err(Error::Drive(DriveError::NotSupported(
539 INDEXED_INNER_UNWRAPPABLE,
540 )));
541 }
542 _ => {
543 return Err(Error::Drive(DriveError::NotSupported(
544 "NonCounted-wrapping is only supported for NormalTree, CountTree, and \
545 ProvableCountTree. For sum-bearing continuations under a sum or \
546 count+sum parent, use `for_known_path_key_empty_not_summed_tree` or \
547 `for_known_path_key_empty_not_counted_or_summed_tree` instead.",
548 )));
549 }
550 };
551 let tree = Element::new_non_counted(inner)?;
563 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
564 path, key, tree,
565 ))
566 }
567
568 pub fn for_known_path_key_empty_not_summed_tree(
577 path: Vec<Vec<u8>>,
578 key: Vec<u8>,
579 tree_type: TreeType,
580 storage_flags: Option<&StorageFlags>,
581 ) -> Result<Self, Error> {
582 let element_flags = storage_flags.map(|s| s.to_element_flags());
583 let inner = match tree_type {
584 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
585 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
586 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
587 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
588 TreeType::ProvableCountSumTree => {
589 Element::empty_provable_count_sum_tree_with_flags(element_flags)
590 }
591 TreeType::ProvableCountProvableSumTree => {
592 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
593 }
594 TreeType::ProvableSumIndexedTree
595 | TreeType::ProvableCountIndexedTree
596 | TreeType::ProvableCountProvableSumIndexedTree => {
597 return Err(Error::Drive(DriveError::NotSupported(
598 INDEXED_INNER_UNWRAPPABLE,
599 )));
600 }
601 _ => {
602 return Err(Error::Drive(DriveError::NotSupported(
603 "NotSummed-wrapping is only supported for the six sum-bearing tree \
604 variants (SumTree, BigSumTree, ProvableSumTree, CountSumTree, \
605 ProvableCountSumTree, ProvableCountProvableSumTree).",
606 )));
607 }
608 };
609 let tree = Element::new_not_summed(inner).map_err(|_| {
610 Error::Drive(DriveError::NotSupported(
611 "Element::new_not_summed rejected the inner tree (unreachable given the \
612 match above).",
613 ))
614 })?;
615 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
616 path, key, tree,
617 ))
618 }
619
620 pub fn wrap_in_non_aggregated_for_parent_tree_type(
649 path: Vec<Vec<u8>>,
650 key: Vec<u8>,
651 aggregating_parent_tree_type: TreeType,
652 inner_tree_type: TreeType,
653 storage_flags: Option<&StorageFlags>,
654 ) -> Result<Self, Error> {
655 match aggregating_parent_tree_type {
656 TreeType::CountTree
665 | TreeType::ProvableCountTree
666 | TreeType::ProvableCountIndexedTree => {
667 Self::for_known_path_key_empty_non_counted_tree(
668 path,
669 key,
670 inner_tree_type,
671 storage_flags,
672 )
673 }
674 TreeType::SumTree
678 | TreeType::BigSumTree
679 | TreeType::ProvableSumTree
680 | TreeType::ProvableSumIndexedTree => Self::for_known_path_key_empty_not_summed_tree(
681 path,
682 key,
683 inner_tree_type,
684 storage_flags,
685 ),
686 TreeType::CountSumTree
689 | TreeType::ProvableCountSumTree
690 | TreeType::ProvableCountProvableSumTree
691 | TreeType::ProvableCountProvableSumIndexedTree => {
692 Self::for_known_path_key_empty_not_counted_or_summed_tree(
693 path,
694 key,
695 inner_tree_type,
696 storage_flags,
697 )
698 }
699 _ => Err(Error::Drive(DriveError::NotSupported(
700 "wrap_in_non_aggregated_for_parent_tree_type called with a non-aggregating \
701 parent tree type — caller should use the unwrapped \
702 `empty_tree_operation_for_known_path_key` path instead.",
703 ))),
704 }
705 }
706
707 pub fn for_known_path_key_empty_not_counted_or_summed_tree(
715 path: Vec<Vec<u8>>,
716 key: Vec<u8>,
717 tree_type: TreeType,
718 storage_flags: Option<&StorageFlags>,
719 ) -> Result<Self, Error> {
720 let element_flags = storage_flags.map(|s| s.to_element_flags());
721 let inner = match tree_type {
722 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
723 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
724 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
725 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
726 TreeType::ProvableCountSumTree => {
727 Element::empty_provable_count_sum_tree_with_flags(element_flags)
728 }
729 TreeType::ProvableCountProvableSumTree => {
730 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
731 }
732 TreeType::ProvableSumIndexedTree
733 | TreeType::ProvableCountIndexedTree
734 | TreeType::ProvableCountProvableSumIndexedTree => {
735 return Err(Error::Drive(DriveError::NotSupported(
736 INDEXED_INNER_UNWRAPPABLE,
737 )));
738 }
739 _ => {
740 return Err(Error::Drive(DriveError::NotSupported(
741 "NotCountedOrSummed-wrapping is only supported for the six sum-bearing \
742 tree variants — see `for_known_path_key_empty_not_summed_tree`.",
743 )));
744 }
745 };
746 let tree = Element::new_not_counted_or_summed(inner).map_err(|_| {
747 Error::Drive(DriveError::NotSupported(
748 "Element::new_not_counted_or_summed rejected the inner tree (unreachable \
749 given the match above).",
750 ))
751 })?;
752 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
753 path, key, tree,
754 ))
755 }
756
757 pub fn for_known_path_key_empty_tree_contributing_zero_to_parent(
796 path: Vec<Vec<u8>>,
797 key: Vec<u8>,
798 aggregating_parent_tree_type: TreeType,
799 inner_tree_type: TreeType,
800 storage_flags: Option<&StorageFlags>,
801 ) -> Result<Self, Error> {
802 if matches!(
811 inner_tree_type,
812 TreeType::ProvableSumIndexedTree
813 | TreeType::ProvableCountIndexedTree
814 | TreeType::ProvableCountProvableSumIndexedTree
815 ) {
816 return Err(Error::Drive(DriveError::NotSupported(
817 INDEXED_INNER_UNWRAPPABLE,
818 )));
819 }
820 let inner_is_sum_bearing = matches!(
821 inner_tree_type,
822 TreeType::SumTree
823 | TreeType::BigSumTree
824 | TreeType::ProvableSumTree
825 | TreeType::CountSumTree
826 | TreeType::ProvableCountSumTree
827 | TreeType::ProvableCountProvableSumTree
828 );
829 match aggregating_parent_tree_type {
830 TreeType::CountTree => Self::for_known_path_key_empty_non_counted_any_tree(
831 path,
832 key,
833 inner_tree_type,
834 storage_flags,
835 ),
836 TreeType::CountSumTree => {
837 if inner_is_sum_bearing {
838 Self::for_known_path_key_empty_not_counted_or_summed_tree(
839 path,
840 key,
841 inner_tree_type,
842 storage_flags,
843 )
844 } else {
845 Self::for_known_path_key_empty_non_counted_any_tree(
846 path,
847 key,
848 inner_tree_type,
849 storage_flags,
850 )
851 }
852 }
853 TreeType::SumTree | TreeType::BigSumTree | TreeType::ProvableSumTree => {
854 if inner_is_sum_bearing {
855 Self::for_known_path_key_empty_not_summed_tree(
856 path,
857 key,
858 inner_tree_type,
859 storage_flags,
860 )
861 } else {
862 inner_tree_type.empty_tree_operation_for_known_path_key(
863 path,
864 key,
865 storage_flags,
866 )
867 }
868 }
869 TreeType::ProvableCountIndexedTree
879 | TreeType::ProvableSumIndexedTree
880 | TreeType::ProvableCountProvableSumIndexedTree => {
881 Err(Error::Drive(DriveError::NotSupported(
882 "indexed trees are property-name trees, never value trees, so they cannot \
883 host zero-contributing continuation children — see \
884 crate::drive::document::ranked_index_tree_type.",
885 )))
886 }
887 TreeType::ProvableCountTree
888 | TreeType::ProvableCountSumTree
889 | TreeType::ProvableCountProvableSumTree => {
890 Err(Error::Drive(DriveError::NotSupported(
891 "provable count-bearing parents cannot host zero-contributing children — \
892 grovedb commits their count into every node hash and rejects NonCounted / \
893 NotCountedOrSummed children; the index walker must demote such value trees \
894 to CountSumTree before hanging continuations under them (see \
895 index_level_tree_types_with_continuation_demotion).",
896 )))
897 }
898 _ => Err(Error::Drive(DriveError::NotSupported(
899 "for_known_path_key_empty_tree_contributing_zero_to_parent called with a \
900 non-aggregating parent tree type — caller should use the unwrapped \
901 `empty_tree_operation_for_known_path_key` path instead.",
902 ))),
903 }
904 }
905
906 pub fn for_known_path_key_empty_non_counted_any_tree(
923 path: Vec<Vec<u8>>,
924 key: Vec<u8>,
925 tree_type: TreeType,
926 storage_flags: Option<&StorageFlags>,
927 ) -> Result<Self, Error> {
928 let element_flags = storage_flags.map(|s| s.to_element_flags());
929 let inner = match tree_type {
930 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
931 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
932 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
933 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
934 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
935 TreeType::ProvableCountTree => {
936 Element::empty_provable_count_tree_with_flags(element_flags)
937 }
938 TreeType::ProvableCountSumTree => {
939 Element::empty_provable_count_sum_tree_with_flags(element_flags)
940 }
941 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
942 TreeType::ProvableCountProvableSumTree => {
943 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
944 }
945 TreeType::ProvableSumIndexedTree
946 | TreeType::ProvableCountIndexedTree
947 | TreeType::ProvableCountProvableSumIndexedTree => {
948 return Err(Error::Drive(DriveError::NotSupported(
949 INDEXED_INNER_UNWRAPPABLE,
950 )));
951 }
952 _ => {
953 return Err(Error::Drive(DriveError::NotSupported(
954 "NonCounted-wrapping is only supported for the nine standard merk tree \
955 variants; special trees (commitment / MMR / bulk-append / dense) are \
956 never index continuation trees.",
957 )));
958 }
959 };
960 let tree = Element::new_non_counted(inner)?;
961 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
962 path, key, tree,
963 ))
964 }
965
966 pub fn for_known_path_key_empty_provable_count_tree(
968 path: Vec<Vec<u8>>,
969 key: Vec<u8>,
970 storage_flags: Option<&StorageFlags>,
971 ) -> Self {
972 let tree = match storage_flags {
973 Some(storage_flags) => Element::new_provable_count_tree_with_flags(
974 None,
975 storage_flags.to_some_element_flags(),
976 ),
977 None => Element::empty_provable_count_tree(),
978 };
979
980 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
981 }
982
983 pub fn for_known_path_key_empty_provable_sum_tree(
993 path: Vec<Vec<u8>>,
994 key: Vec<u8>,
995 storage_flags: Option<&StorageFlags>,
996 ) -> Self {
997 let tree = match storage_flags {
998 Some(storage_flags) => Element::new_provable_sum_tree_with_flags(
999 None,
1000 storage_flags.to_some_element_flags(),
1001 ),
1002 None => Element::empty_provable_sum_tree(),
1003 };
1004
1005 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1006 }
1007
1008 pub fn for_known_path_key_empty_provable_count_sum_tree(
1017 path: Vec<Vec<u8>>,
1018 key: Vec<u8>,
1019 storage_flags: Option<&StorageFlags>,
1020 ) -> Self {
1021 let tree = match storage_flags {
1022 Some(storage_flags) => Element::new_provable_count_sum_tree_with_flags(
1023 None,
1024 storage_flags.to_some_element_flags(),
1025 ),
1026 None => Element::empty_provable_count_sum_tree(),
1027 };
1028
1029 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1030 }
1031
1032 pub fn for_known_path_key_empty_provable_count_provable_sum_tree(
1048 path: Vec<Vec<u8>>,
1049 key: Vec<u8>,
1050 storage_flags: Option<&StorageFlags>,
1051 ) -> Self {
1052 let tree = match storage_flags {
1053 Some(storage_flags) => Element::new_provable_count_provable_sum_tree_with_flags(
1054 None,
1055 storage_flags.to_some_element_flags(),
1056 ),
1057 None => Element::empty_provable_count_provable_sum_tree(),
1058 };
1059
1060 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1061 }
1062
1063 pub fn for_estimated_path_key_empty_tree(
1065 path: KeyInfoPath,
1066 key: KeyInfo,
1067 storage_flags: Option<&StorageFlags>,
1068 ) -> Self {
1069 let tree = match storage_flags {
1070 Some(storage_flags) => {
1071 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
1072 }
1073 None => Element::empty_tree(),
1074 };
1075
1076 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1077 }
1078
1079 pub fn for_estimated_path_key_empty_sum_tree(
1081 path: KeyInfoPath,
1082 key: KeyInfo,
1083 storage_flags: Option<&StorageFlags>,
1084 ) -> Self {
1085 let tree = match storage_flags {
1086 Some(storage_flags) => {
1087 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
1088 }
1089 None => Element::empty_sum_tree(),
1090 };
1091
1092 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1093 }
1094
1095 pub fn for_estimated_path_key_empty_count_tree(
1097 path: KeyInfoPath,
1098 key: KeyInfo,
1099 storage_flags: Option<&StorageFlags>,
1100 ) -> Self {
1101 let tree = match storage_flags {
1102 Some(storage_flags) => {
1103 Element::empty_count_tree_with_flags(storage_flags.to_some_element_flags())
1104 }
1105 None => Element::empty_count_tree(),
1106 };
1107
1108 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1109 }
1110
1111 pub fn for_estimated_path_key_empty_provable_count_tree(
1113 path: KeyInfoPath,
1114 key: KeyInfo,
1115 storage_flags: Option<&StorageFlags>,
1116 ) -> Self {
1117 let tree = match storage_flags {
1118 Some(storage_flags) => {
1119 Element::empty_provable_count_tree_with_flags(storage_flags.to_some_element_flags())
1120 }
1121 None => Element::empty_provable_count_tree(),
1122 };
1123
1124 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1125 }
1126
1127 pub fn for_estimated_path_key_empty_provable_sum_tree(
1130 path: KeyInfoPath,
1131 key: KeyInfo,
1132 storage_flags: Option<&StorageFlags>,
1133 ) -> Self {
1134 let tree = match storage_flags {
1135 Some(storage_flags) => {
1136 Element::empty_provable_sum_tree_with_flags(storage_flags.to_some_element_flags())
1137 }
1138 None => Element::empty_provable_sum_tree(),
1139 };
1140
1141 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1142 }
1143
1144 pub fn for_estimated_path_key_empty_count_sum_tree(
1147 path: KeyInfoPath,
1148 key: KeyInfo,
1149 storage_flags: Option<&StorageFlags>,
1150 ) -> Self {
1151 let tree = match storage_flags {
1152 Some(storage_flags) => {
1153 Element::empty_count_sum_tree_with_flags(storage_flags.to_some_element_flags())
1154 }
1155 None => Element::empty_count_sum_tree(),
1156 };
1157
1158 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1159 }
1160
1161 pub fn for_estimated_path_key_empty_provable_count_sum_tree(
1165 path: KeyInfoPath,
1166 key: KeyInfo,
1167 storage_flags: Option<&StorageFlags>,
1168 ) -> Self {
1169 let tree = match storage_flags {
1170 Some(storage_flags) => Element::empty_provable_count_sum_tree_with_flags(
1171 storage_flags.to_some_element_flags(),
1172 ),
1173 None => Element::empty_provable_count_sum_tree(),
1174 };
1175
1176 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1177 }
1178
1179 pub fn for_estimated_path_key_empty_provable_count_provable_sum_tree(
1183 path: KeyInfoPath,
1184 key: KeyInfo,
1185 storage_flags: Option<&StorageFlags>,
1186 ) -> Self {
1187 let tree = match storage_flags {
1188 Some(storage_flags) => Element::empty_provable_count_provable_sum_tree_with_flags(
1189 storage_flags.to_some_element_flags(),
1190 ),
1191 None => Element::empty_provable_count_provable_sum_tree(),
1192 };
1193
1194 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1195 }
1196
1197 pub fn for_known_path_key_empty_provable_count_indexed_tree(
1214 path: Vec<Vec<u8>>,
1215 key: Vec<u8>,
1216 storage_flags: Option<&StorageFlags>,
1217 ) -> Self {
1218 let tree = match storage_flags {
1219 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1220 storage_flags.to_some_element_flags(),
1221 ),
1222 None => Element::empty_provable_count_indexed_tree(),
1223 };
1224
1225 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1226 }
1227
1228 pub fn for_known_path_key_empty_provable_sum_indexed_tree(
1234 path: Vec<Vec<u8>>,
1235 key: Vec<u8>,
1236 storage_flags: Option<&StorageFlags>,
1237 ) -> Self {
1238 let tree = match storage_flags {
1239 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1240 storage_flags.to_some_element_flags(),
1241 ),
1242 None => Element::empty_provable_sum_indexed_tree(),
1243 };
1244
1245 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1246 }
1247
1248 pub fn for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1261 path: Vec<Vec<u8>>,
1262 key: Vec<u8>,
1263 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1264 storage_flags: Option<&StorageFlags>,
1265 ) -> Result<Self, Error> {
1266 let tree = match storage_flags {
1267 Some(storage_flags) => {
1268 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1269 ranked_axes,
1270 storage_flags.to_some_element_flags(),
1271 )?
1272 }
1273 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1274 };
1275
1276 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1277 path, key, tree,
1278 ))
1279 }
1280
1281 pub fn for_known_path_key_empty_indexed_tree(
1291 path: Vec<Vec<u8>>,
1292 key: Vec<u8>,
1293 tree_type: TreeType,
1294 ranked_axes: &[IndexAxis],
1295 storage_flags: Option<&StorageFlags>,
1296 ) -> Result<Self, Error> {
1297 match (tree_type, ranked_axes) {
1298 (TreeType::ProvableCountIndexedTree, [IndexAxis::Count]) => {
1299 Ok(Self::for_known_path_key_empty_provable_count_indexed_tree(
1300 path,
1301 key,
1302 storage_flags,
1303 ))
1304 }
1305 (TreeType::ProvableSumIndexedTree, [IndexAxis::Sum]) => Ok(
1306 Self::for_known_path_key_empty_provable_sum_indexed_tree(path, key, storage_flags),
1307 ),
1308 (TreeType::ProvableCountProvableSumIndexedTree, axes) if !axes.is_empty() => {
1309 Self::for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1310 path,
1311 key,
1312 axes.iter().map(|axis| (axis.tag(), None)).collect(),
1313 storage_flags,
1314 )
1315 }
1316 _ => Err(Error::Drive(DriveError::NotSupported(
1317 "for_known_path_key_empty_indexed_tree called with a tree type / ranked-axis \
1318 pair that does not describe an indexed tree — the single-axis PCIT / PSIT \
1319 variants accept exactly their own axis and PCPSIT needs a non-empty axis list.",
1320 ))),
1321 }
1322 }
1323
1324 pub fn for_estimated_path_key_empty_provable_count_indexed_tree(
1327 path: KeyInfoPath,
1328 key: KeyInfo,
1329 storage_flags: Option<&StorageFlags>,
1330 ) -> Self {
1331 let tree = match storage_flags {
1332 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1333 storage_flags.to_some_element_flags(),
1334 ),
1335 None => Element::empty_provable_count_indexed_tree(),
1336 };
1337
1338 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1339 }
1340
1341 pub fn for_estimated_path_key_empty_provable_sum_indexed_tree(
1344 path: KeyInfoPath,
1345 key: KeyInfo,
1346 storage_flags: Option<&StorageFlags>,
1347 ) -> Self {
1348 let tree = match storage_flags {
1349 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1350 storage_flags.to_some_element_flags(),
1351 ),
1352 None => Element::empty_provable_sum_indexed_tree(),
1353 };
1354
1355 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1356 }
1357
1358 pub fn for_estimated_path_key_empty_provable_count_provable_sum_indexed_tree(
1361 path: KeyInfoPath,
1362 key: KeyInfo,
1363 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1364 storage_flags: Option<&StorageFlags>,
1365 ) -> Result<Self, Error> {
1366 let tree = match storage_flags {
1367 Some(storage_flags) => {
1368 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1369 ranked_axes,
1370 storage_flags.to_some_element_flags(),
1371 )?
1372 }
1373 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1374 };
1375
1376 Ok(LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree))
1377 }
1378
1379 pub fn insert_for_known_path_key_element(
1381 path: Vec<Vec<u8>>,
1382 key: Vec<u8>,
1383 element: Element,
1384 ) -> Self {
1385 GroveOperation(QualifiedGroveDbOp::insert_or_replace_op(path, key, element))
1386 }
1387
1388 pub fn replace_for_known_path_key_element(
1390 path: Vec<Vec<u8>>,
1391 key: Vec<u8>,
1392 element: Element,
1393 ) -> Self {
1394 GroveOperation(QualifiedGroveDbOp::replace_op(path, key, element))
1395 }
1396
1397 pub fn patch_for_known_path_key_element(
1400 path: Vec<Vec<u8>>,
1401 key: Vec<u8>,
1402 element: Element,
1403 change_in_bytes: i32,
1404 ) -> Self {
1405 GroveOperation(QualifiedGroveDbOp::patch_op(
1406 path,
1407 key,
1408 element,
1409 change_in_bytes,
1410 ))
1411 }
1412
1413 pub fn insert_for_estimated_path_key_element(
1415 path: KeyInfoPath,
1416 key: KeyInfo,
1417 element: Element,
1418 ) -> Self {
1419 GroveOperation(QualifiedGroveDbOp::insert_estimated_op(path, key, element))
1420 }
1421
1422 pub fn replace_for_estimated_path_key_element(
1424 path: KeyInfoPath,
1425 key: KeyInfo,
1426 element: Element,
1427 ) -> Self {
1428 GroveOperation(QualifiedGroveDbOp::replace_estimated_op(path, key, element))
1429 }
1430
1431 pub fn refresh_reference_for_known_path_key_reference_info(
1433 path: Vec<Vec<u8>>,
1434 key: Vec<u8>,
1435 reference_path_type: ReferencePathType,
1436 max_reference_hop: MaxReferenceHop,
1437 flags: Option<ElementFlags>,
1438 trust_refresh_reference: bool,
1439 ) -> Self {
1440 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
1441 path,
1442 key,
1443 reference_path_type,
1444 max_reference_hop,
1445 flags,
1446 false,
1453 trust_refresh_reference,
1454 ))
1455 }
1456
1457 pub fn refresh_reference_with_sum_item_for_known_path_key_reference_info(
1471 path: Vec<Vec<u8>>,
1472 key: Vec<u8>,
1473 reference_path_type: ReferencePathType,
1474 max_reference_hop: MaxReferenceHop,
1475 sum_value: i64,
1476 flags: Option<ElementFlags>,
1477 trust_refresh_reference: bool,
1478 ) -> Self {
1479 GroveOperation(QualifiedGroveDbOp::refresh_reference_with_sum_item_op(
1480 path,
1481 key,
1482 reference_path_type,
1483 max_reference_hop,
1484 sum_value,
1485 flags,
1486 false,
1491 trust_refresh_reference,
1492 ))
1493 }
1494}
1495
1496pub trait LowLevelDriveOperationTreeTypeConverter {
1498 fn empty_tree_operation_for_known_path_key(
1500 &self,
1501 path: Vec<Vec<u8>>,
1502 key: Vec<u8>,
1503 storage_flags: Option<&StorageFlags>,
1504 ) -> Result<LowLevelDriveOperation, Error>;
1505}
1506
1507impl LowLevelDriveOperationTreeTypeConverter for TreeType {
1508 fn empty_tree_operation_for_known_path_key(
1510 &self,
1511 path: Vec<Vec<u8>>,
1512 key: Vec<u8>,
1513 storage_flags: Option<&StorageFlags>,
1514 ) -> Result<LowLevelDriveOperation, Error> {
1515 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
1516 let element = match self {
1517 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1518 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1519 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1520 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1521 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1522 TreeType::ProvableCountTree => {
1523 Element::empty_provable_count_tree_with_flags(element_flags)
1524 }
1525 TreeType::ProvableCountSumTree => {
1526 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1527 }
1528 TreeType::ProvableCountProvableSumTree => {
1529 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1530 }
1531 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1532 TreeType::CommitmentTree(chunk_power) => {
1533 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
1534 }
1535 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
1536 TreeType::BulkAppendTree(chunk_power) => {
1537 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
1538 }
1539 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
1540 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
1541 }
1542 TreeType::ProvableSumIndexedTree => {
1547 Element::empty_provable_sum_indexed_tree_with_flags(element_flags)
1548 }
1549 TreeType::ProvableCountIndexedTree => {
1550 Element::empty_provable_count_indexed_tree_with_flags(element_flags)
1551 }
1552 TreeType::ProvableCountProvableSumIndexedTree => {
1564 return Err(Error::Drive(DriveError::NotSupported(
1565 "empty_tree_operation_for_known_path_key cannot create a \
1566 ProvableCountProvableSumIndexedTree — the ranked axis set is not carried \
1567 by TreeType; use for_known_path_key_empty_indexed_tree (or \
1568 batch_insert_empty_provable_count_provable_sum_indexed_tree) instead.",
1569 )))
1570 }
1571 TreeType::PrivateDocumentStore(_) => {
1577 return Err(Error::Drive(DriveError::NotSupported(
1578 "empty_tree_operation_for_known_path_key cannot create a \
1579 PrivateDocumentStore — the entry size is not carried by TreeType",
1580 )))
1581 }
1582 };
1583
1584 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1585 path, key, element,
1586 ))
1587 }
1588}
1589
1590pub trait DriveCost {
1592 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
1594}
1595
1596impl DriveCost for OperationCost {
1597 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
1599 let OperationCost {
1600 seek_count,
1601 storage_cost,
1602 storage_loaded_bytes,
1603 hash_node_calls,
1604 sinsemilla_hash_calls,
1605 } = self;
1606 let epoch_cost_for_processing_credit_per_byte =
1607 fee_version.storage.storage_processing_credit_per_byte;
1608 let seek_cost = (*seek_count as u64)
1609 .checked_mul(fee_version.storage.storage_seek_cost)
1610 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
1611 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
1612 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1613 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1614 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
1615 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1616 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1617 let storage_removed_bytes_ephemeral_cost =
1618 (storage_cost.removed_bytes.total_removed_bytes() as u64)
1619 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1620 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1621 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
1623 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
1624 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
1625
1626 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
1628 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
1630 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
1631 seek_cost
1632 .checked_add(storage_added_bytes_ephemeral_cost)
1633 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
1634 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
1635 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
1636 .and_then(|c| c.checked_add(hash_node_cost))
1637 .and_then(|c| c.checked_add(sinsemilla_cost))
1638 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
1639 }
1640}
1641
1642#[cfg(test)]
1643#[allow(clippy::identity_op)]
1644mod tests {
1645 use super::*;
1646 use grovedb_costs::storage_cost::removal::StorageRemovedBytes;
1647 use grovedb_costs::storage_cost::StorageCost;
1648 use platform_version::version::fee::storage::FeeStorageVersion;
1649 use platform_version::version::fee::FeeVersion;
1650
1651 fn fee_version() -> &'static FeeVersion {
1653 FeeVersion::first()
1654 }
1655
1656 #[test]
1661 fn base_op_stop_costs_zero() {
1662 assert_eq!(BaseOp::Stop.cost(), 0);
1663 }
1664
1665 #[test]
1666 fn base_op_add_costs_12() {
1667 assert_eq!(BaseOp::Add.cost(), 12);
1668 }
1669
1670 #[test]
1671 fn base_op_mul_costs_20() {
1672 assert_eq!(BaseOp::Mul.cost(), 20);
1673 }
1674
1675 #[test]
1676 fn base_op_signextend_costs_20() {
1677 assert_eq!(BaseOp::Signextend.cost(), 20);
1678 }
1679
1680 #[test]
1681 fn base_op_addmod_costs_32() {
1682 assert_eq!(BaseOp::Addmod.cost(), 32);
1683 }
1684
1685 #[test]
1686 fn base_op_mulmod_costs_32() {
1687 assert_eq!(BaseOp::Mulmod.cost(), 32);
1688 }
1689
1690 #[test]
1691 fn base_op_byte_costs_12() {
1692 assert_eq!(BaseOp::Byte.cost(), 12);
1693 }
1694
1695 #[test]
1696 fn base_op_sub_costs_12() {
1697 assert_eq!(BaseOp::Sub.cost(), 12);
1698 }
1699
1700 #[test]
1701 fn base_op_div_costs_20() {
1702 assert_eq!(BaseOp::Div.cost(), 20);
1703 }
1704
1705 #[test]
1706 fn base_op_comparison_ops_all_cost_12() {
1707 for op in [
1708 BaseOp::Lt,
1709 BaseOp::Gt,
1710 BaseOp::Slt,
1711 BaseOp::Sgt,
1712 BaseOp::Eq,
1713 BaseOp::Iszero,
1714 ] {
1715 assert_eq!(op.cost(), 12, "comparison op {:?} should cost 12", op);
1716 }
1717 }
1718
1719 #[test]
1720 fn base_op_bitwise_ops_all_cost_12() {
1721 for op in [BaseOp::And, BaseOp::Or, BaseOp::Xor, BaseOp::Not] {
1722 assert_eq!(op.cost(), 12, "bitwise op {:?} should cost 12", op);
1723 }
1724 }
1725
1726 #[test]
1731 fn hash_function_block_size_all_64() {
1732 assert_eq!(HashFunction::Sha256.block_size(), 64);
1734 assert_eq!(HashFunction::Sha256_2.block_size(), 64);
1735 assert_eq!(HashFunction::Blake3.block_size(), 64);
1736 assert_eq!(HashFunction::Sha256RipeMD160.block_size(), 64);
1737 }
1738
1739 #[test]
1740 fn hash_function_rounds() {
1741 assert_eq!(HashFunction::Sha256.rounds(), 1);
1742 assert_eq!(HashFunction::Sha256_2.rounds(), 2);
1743 assert_eq!(HashFunction::Blake3.rounds(), 1);
1744 assert_eq!(HashFunction::Sha256RipeMD160.rounds(), 1);
1745 }
1746
1747 #[test]
1748 fn hash_function_block_cost_sha256_variants_use_sha256_per_block() {
1749 let fv = fee_version();
1750 let expected = fv.hashing.sha256_per_block;
1751 assert_eq!(HashFunction::Sha256.block_cost(fv), expected);
1752 assert_eq!(HashFunction::Sha256_2.block_cost(fv), expected);
1753 assert_eq!(HashFunction::Sha256RipeMD160.block_cost(fv), expected);
1754 }
1755
1756 #[test]
1757 fn hash_function_block_cost_blake3_uses_blake3_per_block() {
1758 let fv = fee_version();
1759 assert_eq!(
1760 HashFunction::Blake3.block_cost(fv),
1761 fv.hashing.blake3_per_block
1762 );
1763 }
1764
1765 #[test]
1766 fn hash_function_base_cost_sha256() {
1767 let fv = fee_version();
1768 assert_eq!(
1769 HashFunction::Sha256.base_cost(fv),
1770 fv.hashing.single_sha256_base
1771 );
1772 }
1773
1774 #[test]
1775 fn hash_function_base_cost_sha256_2_uses_single_sha256_base() {
1776 let fv = fee_version();
1777 assert_eq!(
1779 HashFunction::Sha256_2.base_cost(fv),
1780 fv.hashing.single_sha256_base
1781 );
1782 }
1783
1784 #[test]
1785 fn hash_function_base_cost_blake3() {
1786 let fv = fee_version();
1787 assert_eq!(HashFunction::Blake3.base_cost(fv), fv.hashing.blake3_base);
1788 }
1789
1790 #[test]
1791 fn hash_function_base_cost_sha256_ripe_md160() {
1792 let fv = fee_version();
1793 assert_eq!(
1794 HashFunction::Sha256RipeMD160.base_cost(fv),
1795 fv.hashing.sha256_ripe_md160_base
1796 );
1797 }
1798
1799 #[test]
1804 fn function_op_new_with_byte_count_small_sha256() {
1805 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 32);
1807 assert_eq!(op.rounds, 1);
1808 assert_eq!(op.hash, HashFunction::Sha256);
1809 }
1810
1811 #[test]
1812 fn function_op_new_with_byte_count_exact_block_boundary_sha256() {
1813 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 64);
1815 assert_eq!(op.rounds, 2);
1816 }
1817
1818 #[test]
1819 fn function_op_new_with_byte_count_large_sha256() {
1820 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 200);
1822 assert_eq!(op.rounds, 4);
1823 }
1824
1825 #[test]
1826 fn function_op_new_with_byte_count_sha256_2_has_extra_round() {
1827 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 32);
1829 assert_eq!(op.rounds, 2);
1830 }
1831
1832 #[test]
1833 fn function_op_new_with_byte_count_sha256_2_large() {
1834 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 200);
1836 assert_eq!(op.rounds, 5);
1837 }
1838
1839 #[test]
1840 fn function_op_new_with_byte_count_blake3_small() {
1841 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 10);
1843 assert_eq!(op.rounds, 1);
1844 assert_eq!(op.hash, HashFunction::Blake3);
1845 }
1846
1847 #[test]
1848 fn function_op_new_with_byte_count_blake3_large() {
1849 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 500);
1851 assert_eq!(op.rounds, 8);
1852 }
1853
1854 #[test]
1855 fn function_op_new_with_byte_count_zero_bytes() {
1856 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 0);
1858 assert_eq!(op.rounds, 1);
1859 }
1860
1861 #[test]
1862 fn function_op_new_with_byte_count_sha256_ripemd160() {
1863 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256RipeMD160, 20);
1865 assert_eq!(op.rounds, 1);
1866 assert_eq!(op.hash, HashFunction::Sha256RipeMD160);
1867 }
1868
1869 #[test]
1874 fn function_op_cost_sha256_one_round() {
1875 let fv = fee_version();
1876 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 1);
1877 let expected = fv.hashing.single_sha256_base + 1 * fv.hashing.sha256_per_block;
1879 assert_eq!(op.cost(fv), expected);
1880 }
1881
1882 #[test]
1883 fn function_op_cost_sha256_2_two_rounds() {
1884 let fv = fee_version();
1885 let op = FunctionOp::new_with_round_count(HashFunction::Sha256_2, 2);
1886 let expected = fv.hashing.single_sha256_base + 2 * fv.hashing.sha256_per_block;
1888 assert_eq!(op.cost(fv), expected);
1889 }
1890
1891 #[test]
1892 fn function_op_cost_blake3_one_round() {
1893 let fv = fee_version();
1894 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 1);
1895 let expected = fv.hashing.blake3_base + 1 * fv.hashing.blake3_per_block;
1897 assert_eq!(op.cost(fv), expected);
1898 }
1899
1900 #[test]
1901 fn function_op_cost_zero_rounds() {
1902 let fv = fee_version();
1903 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 0);
1904 assert_eq!(op.cost(fv), fv.hashing.blake3_base);
1906 }
1907
1908 #[test]
1909 fn function_op_cost_from_byte_count_matches_manual_calc() {
1910 let fv = fee_version();
1911 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 128);
1913 assert_eq!(op.rounds, 3);
1914 let expected = fv.hashing.single_sha256_base + 3 * fv.hashing.sha256_per_block;
1915 assert_eq!(op.cost(fv), expected);
1916 }
1917
1918 #[test]
1919 fn function_op_cost_sha256_ripemd160() {
1920 let fv = fee_version();
1921 let op = FunctionOp::new_with_round_count(HashFunction::Sha256RipeMD160, 1);
1922 let expected = fv.hashing.sha256_ripe_md160_base + 1 * fv.hashing.sha256_per_block;
1923 assert_eq!(op.cost(fv), expected);
1924 }
1925
1926 #[test]
1927 fn function_op_cost_saturating_mul_does_not_panic_on_large_rounds() {
1928 let fv = fee_version();
1929 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, u32::MAX);
1930 let expected_block_cost = (u32::MAX as u64).saturating_mul(fv.hashing.sha256_per_block);
1933 let expected = fv
1934 .hashing
1935 .single_sha256_base
1936 .saturating_add(expected_block_cost);
1937 assert_eq!(op.cost(fv), expected);
1938 }
1939
1940 #[test]
1941 fn function_op_cost_saturates_to_max_with_extreme_fee_version() {
1942 let mut fv = fee_version().clone();
1945 fv.hashing.sha256_per_block = u64::MAX;
1946 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 2);
1947 assert_eq!(op.cost(&fv), u64::MAX);
1949 }
1950
1951 #[test]
1956 fn operation_cost_calculated_cost_operation_returns_cost() {
1957 let cost = OperationCost {
1958 seek_count: 3,
1959 storage_cost: StorageCost {
1960 added_bytes: 100,
1961 replaced_bytes: 50,
1962 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1963 },
1964 storage_loaded_bytes: 200,
1965 hash_node_calls: 5,
1966 sinsemilla_hash_calls: 0,
1967 };
1968 let op = CalculatedCostOperation(cost.clone());
1969 let result = op.operation_cost().expect("should return Ok");
1970 assert_eq!(result, cost);
1971 }
1972
1973 #[test]
1974 fn operation_cost_grove_operation_returns_error() {
1975 let grove_op = LowLevelDriveOperation::insert_for_known_path_key_element(
1976 vec![vec![1, 2, 3]],
1977 vec![4, 5, 6],
1978 Element::empty_tree(),
1979 );
1980 let result = grove_op.operation_cost();
1981 assert!(result.is_err());
1982 let err_msg = format!("{:?}", result.unwrap_err());
1983 assert!(
1984 err_msg.contains("grove operations must be executed"),
1985 "unexpected error: {}",
1986 err_msg
1987 );
1988 }
1989
1990 #[test]
1991 fn operation_cost_pre_calculated_fee_result_returns_error() {
1992 let fee = FeeResult {
1993 storage_fee: 100,
1994 processing_fee: 200,
1995 ..Default::default()
1996 };
1997 let op = PreCalculatedFeeResult(fee);
1998 let result = op.operation_cost();
1999 assert!(result.is_err());
2000 let err_msg = format!("{:?}", result.unwrap_err());
2001 assert!(
2002 err_msg.contains("pre calculated fees should not be requested"),
2003 "unexpected error: {}",
2004 err_msg
2005 );
2006 }
2007
2008 #[test]
2009 fn operation_cost_function_operation_returns_error() {
2010 let func_op = FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1));
2011 let result = func_op.operation_cost();
2012 assert!(result.is_err());
2013 let err_msg = format!("{:?}", result.unwrap_err());
2014 assert!(
2015 err_msg.contains("function operations should not be requested"),
2016 "unexpected error: {}",
2017 err_msg
2018 );
2019 }
2020
2021 #[test]
2026 fn combine_cost_operations_sums_calculated_costs_only() {
2027 let cost1 = OperationCost {
2028 seek_count: 2,
2029 storage_cost: StorageCost {
2030 added_bytes: 10,
2031 replaced_bytes: 0,
2032 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2033 },
2034 storage_loaded_bytes: 50,
2035 hash_node_calls: 1,
2036 sinsemilla_hash_calls: 0,
2037 };
2038 let cost2 = OperationCost {
2039 seek_count: 3,
2040 storage_cost: StorageCost {
2041 added_bytes: 20,
2042 replaced_bytes: 5,
2043 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2044 },
2045 storage_loaded_bytes: 100,
2046 hash_node_calls: 2,
2047 sinsemilla_hash_calls: 1,
2048 };
2049
2050 let operations = vec![
2051 CalculatedCostOperation(cost1.clone()),
2052 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2054 CalculatedCostOperation(cost2.clone()),
2055 PreCalculatedFeeResult(FeeResult::default()),
2057 ];
2058
2059 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2060 assert_eq!(combined.seek_count, 2 + 3);
2061 assert_eq!(combined.storage_cost.added_bytes, 10 + 20);
2062 assert_eq!(combined.storage_cost.replaced_bytes, 0 + 5);
2063 assert_eq!(combined.storage_loaded_bytes, 50 + 100);
2064 assert_eq!(combined.hash_node_calls, 1 + 2);
2065 assert_eq!(combined.sinsemilla_hash_calls, 0 + 1);
2066 }
2067
2068 #[test]
2069 fn combine_cost_operations_empty_list_returns_default() {
2070 let combined = LowLevelDriveOperation::combine_cost_operations(&[]);
2071 assert_eq!(combined, OperationCost::default());
2072 }
2073
2074 #[test]
2075 fn combine_cost_operations_no_calculated_costs_returns_default() {
2076 let operations = vec![
2077 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 2)),
2078 PreCalculatedFeeResult(FeeResult {
2079 processing_fee: 999,
2080 ..Default::default()
2081 }),
2082 ];
2083 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2084 assert_eq!(combined, OperationCost::default());
2085 }
2086
2087 fn make_grove_op(key_byte: u8) -> LowLevelDriveOperation {
2093 LowLevelDriveOperation::insert_for_known_path_key_element(
2094 vec![vec![0]],
2095 vec![key_byte],
2096 Element::new_item(vec![key_byte]),
2097 )
2098 }
2099
2100 fn make_mixed_ops() -> Vec<LowLevelDriveOperation> {
2101 vec![
2102 make_grove_op(1),
2103 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2104 make_grove_op(2),
2105 CalculatedCostOperation(OperationCost::default()),
2106 make_grove_op(3),
2107 ]
2108 }
2109
2110 #[test]
2111 fn grovedb_operations_batch_filters_grove_ops_from_ref() {
2112 let ops = make_mixed_ops();
2113 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2114 assert_eq!(batch.len(), 3);
2115 }
2116
2117 #[test]
2118 fn grovedb_operations_batch_empty_input() {
2119 let batch = LowLevelDriveOperation::grovedb_operations_batch(&[]);
2120 assert!(batch.is_empty());
2121 }
2122
2123 #[test]
2124 fn grovedb_operations_batch_no_grove_ops() {
2125 let ops = vec![
2126 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1)),
2127 CalculatedCostOperation(OperationCost::default()),
2128 ];
2129 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2130 assert!(batch.is_empty());
2131 }
2132
2133 #[test]
2134 fn grovedb_operations_batch_consume_filters_grove_ops() {
2135 let ops = make_mixed_ops();
2136 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(ops);
2137 assert_eq!(batch.len(), 3);
2138 }
2139
2140 #[test]
2141 fn grovedb_operations_batch_consume_empty_input() {
2142 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(vec![]);
2143 assert!(batch.is_empty());
2144 }
2145
2146 #[test]
2147 fn grovedb_operations_batch_consume_with_leftovers_partitions_correctly() {
2148 let ops = make_mixed_ops();
2149 let (batch, leftovers) =
2150 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2151 assert_eq!(batch.len(), 3);
2152 assert_eq!(leftovers.len(), 2);
2153
2154 for leftover in &leftovers {
2156 assert!(
2157 !matches!(leftover, GroveOperation(_)),
2158 "leftovers should not contain GroveOperation variants"
2159 );
2160 }
2161 }
2162
2163 #[test]
2164 fn grovedb_operations_batch_consume_with_leftovers_all_grove() {
2165 let ops = vec![make_grove_op(10), make_grove_op(20)];
2166 let (batch, leftovers) =
2167 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2168 assert_eq!(batch.len(), 2);
2169 assert!(leftovers.is_empty());
2170 }
2171
2172 #[test]
2173 fn grovedb_operations_batch_consume_with_leftovers_no_grove() {
2174 let ops = vec![
2175 CalculatedCostOperation(OperationCost::default()),
2176 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2177 ];
2178 let (batch, leftovers) =
2179 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2180 assert!(batch.is_empty());
2181 assert_eq!(leftovers.len(), 2);
2182 }
2183
2184 #[test]
2185 fn grovedb_operations_batch_consume_with_leftovers_empty() {
2186 let (batch, leftovers) =
2187 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(vec![]);
2188 assert!(batch.is_empty());
2189 assert!(leftovers.is_empty());
2190 }
2191
2192 #[test]
2197 fn ephemeral_cost_zero_operation() {
2198 let fv = fee_version();
2199 let cost = OperationCost::default();
2200 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2201 assert_eq!(result, 0);
2202 }
2203
2204 #[test]
2205 fn ephemeral_cost_seek_only() {
2206 let fv = fee_version();
2207 let cost = OperationCost {
2208 seek_count: 5,
2209 storage_cost: StorageCost::default(),
2210 storage_loaded_bytes: 0,
2211 hash_node_calls: 0,
2212 sinsemilla_hash_calls: 0,
2213 };
2214 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2215 let expected = 5u64 * fv.storage.storage_seek_cost;
2216 assert_eq!(result, expected);
2217 }
2218
2219 #[test]
2220 fn ephemeral_cost_storage_added_bytes() {
2221 let fv = fee_version();
2222 let cost = OperationCost {
2223 seek_count: 0,
2224 storage_cost: StorageCost {
2225 added_bytes: 100,
2226 replaced_bytes: 0,
2227 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2228 },
2229 storage_loaded_bytes: 0,
2230 hash_node_calls: 0,
2231 sinsemilla_hash_calls: 0,
2232 };
2233 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2234 let expected = 100u64 * fv.storage.storage_processing_credit_per_byte;
2235 assert_eq!(result, expected);
2236 }
2237
2238 #[test]
2239 fn ephemeral_cost_storage_replaced_bytes() {
2240 let fv = fee_version();
2241 let cost = OperationCost {
2242 seek_count: 0,
2243 storage_cost: StorageCost {
2244 added_bytes: 0,
2245 replaced_bytes: 50,
2246 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2247 },
2248 storage_loaded_bytes: 0,
2249 hash_node_calls: 0,
2250 sinsemilla_hash_calls: 0,
2251 };
2252 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2253 let expected = 50u64 * fv.storage.storage_processing_credit_per_byte;
2254 assert_eq!(result, expected);
2255 }
2256
2257 #[test]
2258 fn ephemeral_cost_storage_removed_bytes_basic() {
2259 let fv = fee_version();
2260 let cost = OperationCost {
2261 seek_count: 0,
2262 storage_cost: StorageCost {
2263 added_bytes: 0,
2264 replaced_bytes: 0,
2265 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(75),
2266 },
2267 storage_loaded_bytes: 0,
2268 hash_node_calls: 0,
2269 sinsemilla_hash_calls: 0,
2270 };
2271 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2272 let expected = 75u64 * fv.storage.storage_processing_credit_per_byte;
2273 assert_eq!(result, expected);
2274 }
2275
2276 #[test]
2277 fn ephemeral_cost_loaded_bytes() {
2278 let fv = fee_version();
2279 let cost = OperationCost {
2280 seek_count: 0,
2281 storage_cost: StorageCost::default(),
2282 storage_loaded_bytes: 300,
2283 hash_node_calls: 0,
2284 sinsemilla_hash_calls: 0,
2285 };
2286 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2287 let expected = 300u64 * fv.storage.storage_load_credit_per_byte;
2288 assert_eq!(result, expected);
2289 }
2290
2291 #[test]
2292 fn ephemeral_cost_hash_node_calls() {
2293 let fv = fee_version();
2294 let cost = OperationCost {
2295 seek_count: 0,
2296 storage_cost: StorageCost::default(),
2297 storage_loaded_bytes: 0,
2298 hash_node_calls: 10,
2299 sinsemilla_hash_calls: 0,
2300 };
2301 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2302 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2303 let expected = blake3_total * 10;
2304 assert_eq!(result, expected);
2305 }
2306
2307 #[test]
2308 fn ephemeral_cost_sinsemilla_hash_calls() {
2309 let fv = fee_version();
2310 let cost = OperationCost {
2311 seek_count: 0,
2312 storage_cost: StorageCost::default(),
2313 storage_loaded_bytes: 0,
2314 hash_node_calls: 0,
2315 sinsemilla_hash_calls: 3,
2316 };
2317 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2318 let expected = fv.hashing.sinsemilla_base * 3;
2319 assert_eq!(result, expected);
2320 }
2321
2322 #[test]
2323 fn ephemeral_cost_all_components_combined() {
2324 let fv = fee_version();
2325 let cost = OperationCost {
2326 seek_count: 2,
2327 storage_cost: StorageCost {
2328 added_bytes: 10,
2329 replaced_bytes: 20,
2330 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(30),
2331 },
2332 storage_loaded_bytes: 40,
2333 hash_node_calls: 5,
2334 sinsemilla_hash_calls: 1,
2335 };
2336 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2337
2338 let seek_cost = 2u64 * fv.storage.storage_seek_cost;
2339 let processing_per_byte = fv.storage.storage_processing_credit_per_byte;
2340 let added_cost = 10u64 * processing_per_byte;
2341 let replaced_cost = 20u64 * processing_per_byte;
2342 let removed_cost = 30u64 * processing_per_byte;
2343 let loaded_cost = 40u64 * fv.storage.storage_load_credit_per_byte;
2344 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2345 let hash_cost = blake3_total * 5;
2346 let sinsemilla_cost = fv.hashing.sinsemilla_base * 1;
2347
2348 let expected = seek_cost
2349 + added_cost
2350 + replaced_cost
2351 + loaded_cost
2352 + removed_cost
2353 + hash_cost
2354 + sinsemilla_cost;
2355 assert_eq!(result, expected);
2356 }
2357
2358 #[test]
2359 fn ephemeral_cost_overflow_seek_cost() {
2360 let fv = &FeeVersion {
2361 storage: FeeStorageVersion {
2362 storage_seek_cost: u64::MAX,
2363 ..fee_version().storage.clone()
2364 },
2365 ..fee_version().clone()
2366 };
2367 let cost = OperationCost {
2368 seek_count: 2, storage_cost: StorageCost::default(),
2370 storage_loaded_bytes: 0,
2371 hash_node_calls: 0,
2372 sinsemilla_hash_calls: 0,
2373 };
2374 let result = cost.ephemeral_cost(fv);
2375 assert!(result.is_err(), "expected overflow error for seek cost");
2376 }
2377
2378 #[test]
2379 fn ephemeral_cost_overflow_storage_written_bytes() {
2380 let fv = &FeeVersion {
2381 storage: FeeStorageVersion {
2382 storage_processing_credit_per_byte: u64::MAX,
2383 ..fee_version().storage.clone()
2384 },
2385 ..fee_version().clone()
2386 };
2387 let cost = OperationCost {
2388 seek_count: 0,
2389 storage_cost: StorageCost {
2390 added_bytes: 2, replaced_bytes: 0,
2392 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2393 },
2394 storage_loaded_bytes: 0,
2395 hash_node_calls: 0,
2396 sinsemilla_hash_calls: 0,
2397 };
2398 let result = cost.ephemeral_cost(fv);
2399 assert!(
2400 result.is_err(),
2401 "expected overflow error for storage written bytes"
2402 );
2403 }
2404
2405 #[test]
2406 fn ephemeral_cost_overflow_loaded_bytes() {
2407 let fv = &FeeVersion {
2408 storage: FeeStorageVersion {
2409 storage_load_credit_per_byte: u64::MAX,
2410 ..fee_version().storage.clone()
2411 },
2412 ..fee_version().clone()
2413 };
2414 let cost = OperationCost {
2415 seek_count: 0,
2416 storage_cost: StorageCost::default(),
2417 storage_loaded_bytes: 2, hash_node_calls: 0,
2419 sinsemilla_hash_calls: 0,
2420 };
2421 let result = cost.ephemeral_cost(fv);
2422 assert!(
2423 result.is_err(),
2424 "expected overflow error for loaded bytes cost"
2425 );
2426 }
2427
2428 #[test]
2434 fn empty_tree_operation_for_known_path_key_provable_sum_tree() {
2435 use grovedb::batch::GroveOp;
2436
2437 let op = TreeType::ProvableSumTree
2438 .empty_tree_operation_for_known_path_key(vec![b"root".to_vec()], b"k".to_vec(), None)
2439 .expect("empty_tree_operation_for_known_path_key");
2440
2441 match op {
2442 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2443 GroveOp::InsertOrReplace { element } => assert!(
2444 matches!(element, Element::ProvableSumTree(..)),
2445 "expected ProvableSumTree element, got: {:?}",
2446 element
2447 ),
2448 other => panic!("expected GroveOp::InsertOrReplace, got: {:?}", other),
2449 },
2450 other => panic!("expected GroveOperation, got: {:?}", other),
2451 }
2452 }
2453
2454 #[test]
2462 fn zero_contribution_dispatcher_full_matrix() {
2463 use grovedb::batch::GroveOp;
2464
2465 const ALL_INNERS: [TreeType; 9] = [
2466 TreeType::NormalTree,
2467 TreeType::SumTree,
2468 TreeType::BigSumTree,
2469 TreeType::CountTree,
2470 TreeType::CountSumTree,
2471 TreeType::ProvableCountTree,
2472 TreeType::ProvableCountSumTree,
2473 TreeType::ProvableSumTree,
2474 TreeType::ProvableCountProvableSumTree,
2475 ];
2476
2477 fn is_sum_bearing(tree_type: TreeType) -> bool {
2478 matches!(
2479 tree_type,
2480 TreeType::SumTree
2481 | TreeType::BigSumTree
2482 | TreeType::CountSumTree
2483 | TreeType::ProvableCountSumTree
2484 | TreeType::ProvableSumTree
2485 | TreeType::ProvableCountProvableSumTree
2486 )
2487 }
2488
2489 fn element_tree_type(element: &Element) -> TreeType {
2490 match element {
2491 Element::Tree(..) => TreeType::NormalTree,
2492 Element::SumTree(..) => TreeType::SumTree,
2493 Element::BigSumTree(..) => TreeType::BigSumTree,
2494 Element::CountTree(..) => TreeType::CountTree,
2495 Element::CountSumTree(..) => TreeType::CountSumTree,
2496 Element::ProvableCountTree(..) => TreeType::ProvableCountTree,
2497 Element::ProvableCountSumTree(..) => TreeType::ProvableCountSumTree,
2498 Element::ProvableSumTree(..) => TreeType::ProvableSumTree,
2499 Element::ProvableCountProvableSumTree(..) => TreeType::ProvableCountProvableSumTree,
2500 other => panic!("unexpected inner element: {other:?}"),
2501 }
2502 }
2503
2504 #[derive(Debug, PartialEq)]
2505 enum Expected {
2506 NonCounted,
2507 NotSummed,
2508 NotCountedOrSummed,
2509 Unwrapped,
2510 }
2511
2512 let dispatch = |parent: TreeType, inner: TreeType| {
2513 LowLevelDriveOperation::for_known_path_key_empty_tree_contributing_zero_to_parent(
2514 vec![b"root".to_vec()],
2515 b"key".to_vec(),
2516 parent,
2517 inner,
2518 None,
2519 )
2520 };
2521
2522 let assert_cell = |parent: TreeType, inner: TreeType, expected: Expected| {
2523 let op = dispatch(parent, inner).unwrap_or_else(|error| {
2524 panic!("parent {parent:?} inner {inner:?} must be accepted: {error}")
2525 });
2526 let element = match op {
2527 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2528 GroveOp::InsertOrReplace { element } => element,
2529 other => panic!("expected InsertOrReplace, got {other:?}"),
2530 },
2531 other => panic!("expected GroveOperation, got {other:?}"),
2532 };
2533 let (wrapper, produced_inner) = match &element {
2534 Element::NonCounted(inner_element) => {
2535 (Expected::NonCounted, inner_element.as_ref())
2536 }
2537 Element::NotSummed(inner_element) => (Expected::NotSummed, inner_element.as_ref()),
2538 Element::NotCountedOrSummed(inner_element) => {
2539 (Expected::NotCountedOrSummed, inner_element.as_ref())
2540 }
2541 plain => (Expected::Unwrapped, plain),
2542 };
2543 assert_eq!(
2544 wrapper, expected,
2545 "parent {parent:?} inner {inner:?}: wrong wrapper"
2546 );
2547 assert_eq!(
2548 element_tree_type(produced_inner),
2549 inner,
2550 "parent {parent:?} inner {inner:?}: wrong inner tree type"
2551 );
2552 };
2553
2554 for inner in ALL_INNERS {
2556 assert_cell(TreeType::CountTree, inner, Expected::NonCounted);
2557 }
2558 for inner in ALL_INNERS {
2561 let expected = if is_sum_bearing(inner) {
2562 Expected::NotCountedOrSummed
2563 } else {
2564 Expected::NonCounted
2565 };
2566 assert_cell(TreeType::CountSumTree, inner, expected);
2567 }
2568 for parent in [
2571 TreeType::SumTree,
2572 TreeType::BigSumTree,
2573 TreeType::ProvableSumTree,
2574 ] {
2575 for inner in ALL_INNERS {
2576 let expected = if is_sum_bearing(inner) {
2577 Expected::NotSummed
2578 } else {
2579 Expected::Unwrapped
2580 };
2581 assert_cell(parent, inner, expected);
2582 }
2583 }
2584 for parent in [
2589 TreeType::NormalTree,
2590 TreeType::ProvableCountTree,
2591 TreeType::ProvableCountSumTree,
2592 TreeType::ProvableCountProvableSumTree,
2593 ] {
2594 for inner in ALL_INNERS {
2595 assert!(
2596 dispatch(parent, inner).is_err(),
2597 "parent {parent:?} inner {inner:?} must be rejected"
2598 );
2599 }
2600 }
2601
2602 const INDEXED: [TreeType; 3] = [
2608 TreeType::ProvableCountIndexedTree,
2609 TreeType::ProvableSumIndexedTree,
2610 TreeType::ProvableCountProvableSumIndexedTree,
2611 ];
2612 for indexed in INDEXED {
2613 for inner in ALL_INNERS {
2614 assert!(
2615 dispatch(indexed, inner).is_err(),
2616 "indexed parent {indexed:?} inner {inner:?} must be rejected"
2617 );
2618 }
2619 for parent in [
2620 TreeType::CountTree,
2621 TreeType::CountSumTree,
2622 TreeType::SumTree,
2623 TreeType::BigSumTree,
2624 TreeType::ProvableSumTree,
2625 ] {
2626 assert!(
2627 dispatch(parent, indexed).is_err(),
2628 "parent {parent:?} indexed inner {indexed:?} must be rejected"
2629 );
2630 }
2631 }
2632 }
2633
2634 #[test]
2635 fn ephemeral_cost_overflow_in_addition_chain() {
2636 let fv = fee_version();
2638 let cost = OperationCost {
2639 seek_count: u32::MAX,
2640 storage_cost: StorageCost {
2641 added_bytes: u32::MAX,
2642 replaced_bytes: u32::MAX,
2643 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(u32::MAX),
2644 },
2645 storage_loaded_bytes: u64::MAX,
2646 hash_node_calls: u32::MAX,
2647 sinsemilla_hash_calls: u32::MAX,
2648 };
2649 let result = cost.ephemeral_cost(fv);
2650 assert!(
2651 result.is_err(),
2652 "expected overflow error when summing large components"
2653 );
2654 }
2655}