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());
496 let inner = match tree_type {
497 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
498 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
499 TreeType::ProvableCountTree => {
500 Element::empty_provable_count_tree_with_flags(element_flags)
501 }
502 _ => {
503 return Err(Error::Drive(DriveError::NotSupported(
504 "NonCounted-wrapping is only supported for NormalTree, CountTree, and \
505 ProvableCountTree. For sum-bearing continuations under a sum or \
506 count+sum parent, use `for_known_path_key_empty_not_summed_tree` or \
507 `for_known_path_key_empty_not_counted_or_summed_tree` instead.",
508 )));
509 }
510 };
511 let tree = Element::new_non_counted(inner)?;
523 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
524 path, key, tree,
525 ))
526 }
527
528 pub fn for_known_path_key_empty_not_summed_tree(
537 path: Vec<Vec<u8>>,
538 key: Vec<u8>,
539 tree_type: TreeType,
540 storage_flags: Option<&StorageFlags>,
541 ) -> Result<Self, Error> {
542 let element_flags = storage_flags.map(|s| s.to_element_flags());
543 let inner = match tree_type {
544 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
545 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
546 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
547 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
548 TreeType::ProvableCountSumTree => {
549 Element::empty_provable_count_sum_tree_with_flags(element_flags)
550 }
551 TreeType::ProvableCountProvableSumTree => {
552 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
553 }
554 _ => {
555 return Err(Error::Drive(DriveError::NotSupported(
556 "NotSummed-wrapping is only supported for the six sum-bearing tree \
557 variants (SumTree, BigSumTree, ProvableSumTree, CountSumTree, \
558 ProvableCountSumTree, ProvableCountProvableSumTree).",
559 )));
560 }
561 };
562 let tree = Element::new_not_summed(inner).map_err(|_| {
563 Error::Drive(DriveError::NotSupported(
564 "Element::new_not_summed rejected the inner tree (unreachable given the \
565 match above).",
566 ))
567 })?;
568 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
569 path, key, tree,
570 ))
571 }
572
573 pub fn wrap_in_non_aggregated_for_parent_tree_type(
602 path: Vec<Vec<u8>>,
603 key: Vec<u8>,
604 aggregating_parent_tree_type: TreeType,
605 inner_tree_type: TreeType,
606 storage_flags: Option<&StorageFlags>,
607 ) -> Result<Self, Error> {
608 match aggregating_parent_tree_type {
609 TreeType::CountTree | TreeType::ProvableCountTree => {
613 Self::for_known_path_key_empty_non_counted_tree(
614 path,
615 key,
616 inner_tree_type,
617 storage_flags,
618 )
619 }
620 TreeType::SumTree | TreeType::BigSumTree | TreeType::ProvableSumTree => {
624 Self::for_known_path_key_empty_not_summed_tree(
625 path,
626 key,
627 inner_tree_type,
628 storage_flags,
629 )
630 }
631 TreeType::CountSumTree
634 | TreeType::ProvableCountSumTree
635 | TreeType::ProvableCountProvableSumTree => {
636 Self::for_known_path_key_empty_not_counted_or_summed_tree(
637 path,
638 key,
639 inner_tree_type,
640 storage_flags,
641 )
642 }
643 _ => Err(Error::Drive(DriveError::NotSupported(
644 "wrap_in_non_aggregated_for_parent_tree_type called with a non-aggregating \
645 parent tree type — caller should use the unwrapped \
646 `empty_tree_operation_for_known_path_key` path instead.",
647 ))),
648 }
649 }
650
651 pub fn for_known_path_key_empty_not_counted_or_summed_tree(
659 path: Vec<Vec<u8>>,
660 key: Vec<u8>,
661 tree_type: TreeType,
662 storage_flags: Option<&StorageFlags>,
663 ) -> Result<Self, Error> {
664 let element_flags = storage_flags.map(|s| s.to_element_flags());
665 let inner = match tree_type {
666 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
667 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
668 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
669 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
670 TreeType::ProvableCountSumTree => {
671 Element::empty_provable_count_sum_tree_with_flags(element_flags)
672 }
673 TreeType::ProvableCountProvableSumTree => {
674 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
675 }
676 _ => {
677 return Err(Error::Drive(DriveError::NotSupported(
678 "NotCountedOrSummed-wrapping is only supported for the six sum-bearing \
679 tree variants — see `for_known_path_key_empty_not_summed_tree`.",
680 )));
681 }
682 };
683 let tree = Element::new_not_counted_or_summed(inner).map_err(|_| {
684 Error::Drive(DriveError::NotSupported(
685 "Element::new_not_counted_or_summed rejected the inner tree (unreachable \
686 given the match above).",
687 ))
688 })?;
689 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
690 path, key, tree,
691 ))
692 }
693
694 pub fn for_known_path_key_empty_provable_count_tree(
696 path: Vec<Vec<u8>>,
697 key: Vec<u8>,
698 storage_flags: Option<&StorageFlags>,
699 ) -> Self {
700 let tree = match storage_flags {
701 Some(storage_flags) => Element::new_provable_count_tree_with_flags(
702 None,
703 storage_flags.to_some_element_flags(),
704 ),
705 None => Element::empty_provable_count_tree(),
706 };
707
708 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
709 }
710
711 pub fn for_known_path_key_empty_provable_sum_tree(
721 path: Vec<Vec<u8>>,
722 key: Vec<u8>,
723 storage_flags: Option<&StorageFlags>,
724 ) -> Self {
725 let tree = match storage_flags {
726 Some(storage_flags) => Element::new_provable_sum_tree_with_flags(
727 None,
728 storage_flags.to_some_element_flags(),
729 ),
730 None => Element::empty_provable_sum_tree(),
731 };
732
733 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
734 }
735
736 pub fn for_known_path_key_empty_provable_count_sum_tree(
745 path: Vec<Vec<u8>>,
746 key: Vec<u8>,
747 storage_flags: Option<&StorageFlags>,
748 ) -> Self {
749 let tree = match storage_flags {
750 Some(storage_flags) => Element::new_provable_count_sum_tree_with_flags(
751 None,
752 storage_flags.to_some_element_flags(),
753 ),
754 None => Element::empty_provable_count_sum_tree(),
755 };
756
757 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
758 }
759
760 pub fn for_known_path_key_empty_provable_count_provable_sum_tree(
776 path: Vec<Vec<u8>>,
777 key: Vec<u8>,
778 storage_flags: Option<&StorageFlags>,
779 ) -> Self {
780 let tree = match storage_flags {
781 Some(storage_flags) => Element::new_provable_count_provable_sum_tree_with_flags(
782 None,
783 storage_flags.to_some_element_flags(),
784 ),
785 None => Element::empty_provable_count_provable_sum_tree(),
786 };
787
788 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
789 }
790
791 pub fn for_estimated_path_key_empty_tree(
793 path: KeyInfoPath,
794 key: KeyInfo,
795 storage_flags: Option<&StorageFlags>,
796 ) -> Self {
797 let tree = match storage_flags {
798 Some(storage_flags) => {
799 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
800 }
801 None => Element::empty_tree(),
802 };
803
804 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
805 }
806
807 pub fn for_estimated_path_key_empty_sum_tree(
809 path: KeyInfoPath,
810 key: KeyInfo,
811 storage_flags: Option<&StorageFlags>,
812 ) -> Self {
813 let tree = match storage_flags {
814 Some(storage_flags) => {
815 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
816 }
817 None => Element::empty_sum_tree(),
818 };
819
820 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
821 }
822
823 pub fn for_estimated_path_key_empty_count_tree(
825 path: KeyInfoPath,
826 key: KeyInfo,
827 storage_flags: Option<&StorageFlags>,
828 ) -> Self {
829 let tree = match storage_flags {
830 Some(storage_flags) => {
831 Element::empty_count_tree_with_flags(storage_flags.to_some_element_flags())
832 }
833 None => Element::empty_count_tree(),
834 };
835
836 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
837 }
838
839 pub fn for_estimated_path_key_empty_provable_count_tree(
841 path: KeyInfoPath,
842 key: KeyInfo,
843 storage_flags: Option<&StorageFlags>,
844 ) -> Self {
845 let tree = match storage_flags {
846 Some(storage_flags) => {
847 Element::empty_provable_count_tree_with_flags(storage_flags.to_some_element_flags())
848 }
849 None => Element::empty_provable_count_tree(),
850 };
851
852 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
853 }
854
855 pub fn for_estimated_path_key_empty_provable_sum_tree(
858 path: KeyInfoPath,
859 key: KeyInfo,
860 storage_flags: Option<&StorageFlags>,
861 ) -> Self {
862 let tree = match storage_flags {
863 Some(storage_flags) => {
864 Element::empty_provable_sum_tree_with_flags(storage_flags.to_some_element_flags())
865 }
866 None => Element::empty_provable_sum_tree(),
867 };
868
869 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
870 }
871
872 pub fn for_estimated_path_key_empty_count_sum_tree(
875 path: KeyInfoPath,
876 key: KeyInfo,
877 storage_flags: Option<&StorageFlags>,
878 ) -> Self {
879 let tree = match storage_flags {
880 Some(storage_flags) => {
881 Element::empty_count_sum_tree_with_flags(storage_flags.to_some_element_flags())
882 }
883 None => Element::empty_count_sum_tree(),
884 };
885
886 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
887 }
888
889 pub fn for_estimated_path_key_empty_provable_count_sum_tree(
893 path: KeyInfoPath,
894 key: KeyInfo,
895 storage_flags: Option<&StorageFlags>,
896 ) -> Self {
897 let tree = match storage_flags {
898 Some(storage_flags) => Element::empty_provable_count_sum_tree_with_flags(
899 storage_flags.to_some_element_flags(),
900 ),
901 None => Element::empty_provable_count_sum_tree(),
902 };
903
904 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
905 }
906
907 pub fn for_estimated_path_key_empty_provable_count_provable_sum_tree(
911 path: KeyInfoPath,
912 key: KeyInfo,
913 storage_flags: Option<&StorageFlags>,
914 ) -> Self {
915 let tree = match storage_flags {
916 Some(storage_flags) => Element::empty_provable_count_provable_sum_tree_with_flags(
917 storage_flags.to_some_element_flags(),
918 ),
919 None => Element::empty_provable_count_provable_sum_tree(),
920 };
921
922 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
923 }
924
925 pub fn insert_for_known_path_key_element(
927 path: Vec<Vec<u8>>,
928 key: Vec<u8>,
929 element: Element,
930 ) -> Self {
931 GroveOperation(QualifiedGroveDbOp::insert_or_replace_op(path, key, element))
932 }
933
934 pub fn replace_for_known_path_key_element(
936 path: Vec<Vec<u8>>,
937 key: Vec<u8>,
938 element: Element,
939 ) -> Self {
940 GroveOperation(QualifiedGroveDbOp::replace_op(path, key, element))
941 }
942
943 pub fn patch_for_known_path_key_element(
946 path: Vec<Vec<u8>>,
947 key: Vec<u8>,
948 element: Element,
949 change_in_bytes: i32,
950 ) -> Self {
951 GroveOperation(QualifiedGroveDbOp::patch_op(
952 path,
953 key,
954 element,
955 change_in_bytes,
956 ))
957 }
958
959 pub fn insert_for_estimated_path_key_element(
961 path: KeyInfoPath,
962 key: KeyInfo,
963 element: Element,
964 ) -> Self {
965 GroveOperation(QualifiedGroveDbOp::insert_estimated_op(path, key, element))
966 }
967
968 pub fn replace_for_estimated_path_key_element(
970 path: KeyInfoPath,
971 key: KeyInfo,
972 element: Element,
973 ) -> Self {
974 GroveOperation(QualifiedGroveDbOp::replace_estimated_op(path, key, element))
975 }
976
977 pub fn refresh_reference_for_known_path_key_reference_info(
979 path: Vec<Vec<u8>>,
980 key: Vec<u8>,
981 reference_path_type: ReferencePathType,
982 max_reference_hop: MaxReferenceHop,
983 flags: Option<ElementFlags>,
984 trust_refresh_reference: bool,
985 ) -> Self {
986 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
987 path,
988 key,
989 reference_path_type,
990 max_reference_hop,
991 flags,
992 false,
999 trust_refresh_reference,
1000 ))
1001 }
1002
1003 pub fn refresh_reference_with_sum_item_for_known_path_key_reference_info(
1017 path: Vec<Vec<u8>>,
1018 key: Vec<u8>,
1019 reference_path_type: ReferencePathType,
1020 max_reference_hop: MaxReferenceHop,
1021 sum_value: i64,
1022 flags: Option<ElementFlags>,
1023 trust_refresh_reference: bool,
1024 ) -> Self {
1025 GroveOperation(QualifiedGroveDbOp::refresh_reference_with_sum_item_op(
1026 path,
1027 key,
1028 reference_path_type,
1029 max_reference_hop,
1030 sum_value,
1031 flags,
1032 false,
1037 trust_refresh_reference,
1038 ))
1039 }
1040}
1041
1042pub trait LowLevelDriveOperationTreeTypeConverter {
1044 fn empty_tree_operation_for_known_path_key(
1046 &self,
1047 path: Vec<Vec<u8>>,
1048 key: Vec<u8>,
1049 storage_flags: Option<&StorageFlags>,
1050 ) -> Result<LowLevelDriveOperation, Error>;
1051}
1052
1053impl LowLevelDriveOperationTreeTypeConverter for TreeType {
1054 fn empty_tree_operation_for_known_path_key(
1056 &self,
1057 path: Vec<Vec<u8>>,
1058 key: Vec<u8>,
1059 storage_flags: Option<&StorageFlags>,
1060 ) -> Result<LowLevelDriveOperation, Error> {
1061 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
1062 let element = match self {
1063 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1064 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1065 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1066 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1067 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1068 TreeType::ProvableCountTree => {
1069 Element::empty_provable_count_tree_with_flags(element_flags)
1070 }
1071 TreeType::ProvableCountSumTree => {
1072 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1073 }
1074 TreeType::ProvableCountProvableSumTree => {
1075 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1076 }
1077 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1078 TreeType::CommitmentTree(chunk_power) => {
1079 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
1080 }
1081 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
1082 TreeType::BulkAppendTree(chunk_power) => {
1083 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
1084 }
1085 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
1086 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
1087 }
1088 };
1089
1090 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1091 path, key, element,
1092 ))
1093 }
1094}
1095
1096pub trait DriveCost {
1098 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
1100}
1101
1102impl DriveCost for OperationCost {
1103 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
1105 let OperationCost {
1106 seek_count,
1107 storage_cost,
1108 storage_loaded_bytes,
1109 hash_node_calls,
1110 sinsemilla_hash_calls,
1111 } = self;
1112 let epoch_cost_for_processing_credit_per_byte =
1113 fee_version.storage.storage_processing_credit_per_byte;
1114 let seek_cost = (*seek_count as u64)
1115 .checked_mul(fee_version.storage.storage_seek_cost)
1116 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
1117 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
1118 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1119 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1120 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
1121 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1122 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1123 let storage_removed_bytes_ephemeral_cost =
1124 (storage_cost.removed_bytes.total_removed_bytes() as u64)
1125 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1126 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1127 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
1129 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
1130 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
1131
1132 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
1134 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
1136 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
1137 seek_cost
1138 .checked_add(storage_added_bytes_ephemeral_cost)
1139 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
1140 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
1141 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
1142 .and_then(|c| c.checked_add(hash_node_cost))
1143 .and_then(|c| c.checked_add(sinsemilla_cost))
1144 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
1145 }
1146}
1147
1148#[cfg(test)]
1149#[allow(clippy::identity_op)]
1150mod tests {
1151 use super::*;
1152 use grovedb_costs::storage_cost::removal::StorageRemovedBytes;
1153 use grovedb_costs::storage_cost::StorageCost;
1154 use platform_version::version::fee::storage::FeeStorageVersion;
1155 use platform_version::version::fee::FeeVersion;
1156
1157 fn fee_version() -> &'static FeeVersion {
1159 FeeVersion::first()
1160 }
1161
1162 #[test]
1167 fn base_op_stop_costs_zero() {
1168 assert_eq!(BaseOp::Stop.cost(), 0);
1169 }
1170
1171 #[test]
1172 fn base_op_add_costs_12() {
1173 assert_eq!(BaseOp::Add.cost(), 12);
1174 }
1175
1176 #[test]
1177 fn base_op_mul_costs_20() {
1178 assert_eq!(BaseOp::Mul.cost(), 20);
1179 }
1180
1181 #[test]
1182 fn base_op_signextend_costs_20() {
1183 assert_eq!(BaseOp::Signextend.cost(), 20);
1184 }
1185
1186 #[test]
1187 fn base_op_addmod_costs_32() {
1188 assert_eq!(BaseOp::Addmod.cost(), 32);
1189 }
1190
1191 #[test]
1192 fn base_op_mulmod_costs_32() {
1193 assert_eq!(BaseOp::Mulmod.cost(), 32);
1194 }
1195
1196 #[test]
1197 fn base_op_byte_costs_12() {
1198 assert_eq!(BaseOp::Byte.cost(), 12);
1199 }
1200
1201 #[test]
1202 fn base_op_sub_costs_12() {
1203 assert_eq!(BaseOp::Sub.cost(), 12);
1204 }
1205
1206 #[test]
1207 fn base_op_div_costs_20() {
1208 assert_eq!(BaseOp::Div.cost(), 20);
1209 }
1210
1211 #[test]
1212 fn base_op_comparison_ops_all_cost_12() {
1213 for op in [
1214 BaseOp::Lt,
1215 BaseOp::Gt,
1216 BaseOp::Slt,
1217 BaseOp::Sgt,
1218 BaseOp::Eq,
1219 BaseOp::Iszero,
1220 ] {
1221 assert_eq!(op.cost(), 12, "comparison op {:?} should cost 12", op);
1222 }
1223 }
1224
1225 #[test]
1226 fn base_op_bitwise_ops_all_cost_12() {
1227 for op in [BaseOp::And, BaseOp::Or, BaseOp::Xor, BaseOp::Not] {
1228 assert_eq!(op.cost(), 12, "bitwise op {:?} should cost 12", op);
1229 }
1230 }
1231
1232 #[test]
1237 fn hash_function_block_size_all_64() {
1238 assert_eq!(HashFunction::Sha256.block_size(), 64);
1240 assert_eq!(HashFunction::Sha256_2.block_size(), 64);
1241 assert_eq!(HashFunction::Blake3.block_size(), 64);
1242 assert_eq!(HashFunction::Sha256RipeMD160.block_size(), 64);
1243 }
1244
1245 #[test]
1246 fn hash_function_rounds() {
1247 assert_eq!(HashFunction::Sha256.rounds(), 1);
1248 assert_eq!(HashFunction::Sha256_2.rounds(), 2);
1249 assert_eq!(HashFunction::Blake3.rounds(), 1);
1250 assert_eq!(HashFunction::Sha256RipeMD160.rounds(), 1);
1251 }
1252
1253 #[test]
1254 fn hash_function_block_cost_sha256_variants_use_sha256_per_block() {
1255 let fv = fee_version();
1256 let expected = fv.hashing.sha256_per_block;
1257 assert_eq!(HashFunction::Sha256.block_cost(fv), expected);
1258 assert_eq!(HashFunction::Sha256_2.block_cost(fv), expected);
1259 assert_eq!(HashFunction::Sha256RipeMD160.block_cost(fv), expected);
1260 }
1261
1262 #[test]
1263 fn hash_function_block_cost_blake3_uses_blake3_per_block() {
1264 let fv = fee_version();
1265 assert_eq!(
1266 HashFunction::Blake3.block_cost(fv),
1267 fv.hashing.blake3_per_block
1268 );
1269 }
1270
1271 #[test]
1272 fn hash_function_base_cost_sha256() {
1273 let fv = fee_version();
1274 assert_eq!(
1275 HashFunction::Sha256.base_cost(fv),
1276 fv.hashing.single_sha256_base
1277 );
1278 }
1279
1280 #[test]
1281 fn hash_function_base_cost_sha256_2_uses_single_sha256_base() {
1282 let fv = fee_version();
1283 assert_eq!(
1285 HashFunction::Sha256_2.base_cost(fv),
1286 fv.hashing.single_sha256_base
1287 );
1288 }
1289
1290 #[test]
1291 fn hash_function_base_cost_blake3() {
1292 let fv = fee_version();
1293 assert_eq!(HashFunction::Blake3.base_cost(fv), fv.hashing.blake3_base);
1294 }
1295
1296 #[test]
1297 fn hash_function_base_cost_sha256_ripe_md160() {
1298 let fv = fee_version();
1299 assert_eq!(
1300 HashFunction::Sha256RipeMD160.base_cost(fv),
1301 fv.hashing.sha256_ripe_md160_base
1302 );
1303 }
1304
1305 #[test]
1310 fn function_op_new_with_byte_count_small_sha256() {
1311 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 32);
1313 assert_eq!(op.rounds, 1);
1314 assert_eq!(op.hash, HashFunction::Sha256);
1315 }
1316
1317 #[test]
1318 fn function_op_new_with_byte_count_exact_block_boundary_sha256() {
1319 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 64);
1321 assert_eq!(op.rounds, 2);
1322 }
1323
1324 #[test]
1325 fn function_op_new_with_byte_count_large_sha256() {
1326 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 200);
1328 assert_eq!(op.rounds, 4);
1329 }
1330
1331 #[test]
1332 fn function_op_new_with_byte_count_sha256_2_has_extra_round() {
1333 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 32);
1335 assert_eq!(op.rounds, 2);
1336 }
1337
1338 #[test]
1339 fn function_op_new_with_byte_count_sha256_2_large() {
1340 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 200);
1342 assert_eq!(op.rounds, 5);
1343 }
1344
1345 #[test]
1346 fn function_op_new_with_byte_count_blake3_small() {
1347 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 10);
1349 assert_eq!(op.rounds, 1);
1350 assert_eq!(op.hash, HashFunction::Blake3);
1351 }
1352
1353 #[test]
1354 fn function_op_new_with_byte_count_blake3_large() {
1355 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 500);
1357 assert_eq!(op.rounds, 8);
1358 }
1359
1360 #[test]
1361 fn function_op_new_with_byte_count_zero_bytes() {
1362 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 0);
1364 assert_eq!(op.rounds, 1);
1365 }
1366
1367 #[test]
1368 fn function_op_new_with_byte_count_sha256_ripemd160() {
1369 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256RipeMD160, 20);
1371 assert_eq!(op.rounds, 1);
1372 assert_eq!(op.hash, HashFunction::Sha256RipeMD160);
1373 }
1374
1375 #[test]
1380 fn function_op_cost_sha256_one_round() {
1381 let fv = fee_version();
1382 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 1);
1383 let expected = fv.hashing.single_sha256_base + 1 * fv.hashing.sha256_per_block;
1385 assert_eq!(op.cost(fv), expected);
1386 }
1387
1388 #[test]
1389 fn function_op_cost_sha256_2_two_rounds() {
1390 let fv = fee_version();
1391 let op = FunctionOp::new_with_round_count(HashFunction::Sha256_2, 2);
1392 let expected = fv.hashing.single_sha256_base + 2 * fv.hashing.sha256_per_block;
1394 assert_eq!(op.cost(fv), expected);
1395 }
1396
1397 #[test]
1398 fn function_op_cost_blake3_one_round() {
1399 let fv = fee_version();
1400 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 1);
1401 let expected = fv.hashing.blake3_base + 1 * fv.hashing.blake3_per_block;
1403 assert_eq!(op.cost(fv), expected);
1404 }
1405
1406 #[test]
1407 fn function_op_cost_zero_rounds() {
1408 let fv = fee_version();
1409 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 0);
1410 assert_eq!(op.cost(fv), fv.hashing.blake3_base);
1412 }
1413
1414 #[test]
1415 fn function_op_cost_from_byte_count_matches_manual_calc() {
1416 let fv = fee_version();
1417 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 128);
1419 assert_eq!(op.rounds, 3);
1420 let expected = fv.hashing.single_sha256_base + 3 * fv.hashing.sha256_per_block;
1421 assert_eq!(op.cost(fv), expected);
1422 }
1423
1424 #[test]
1425 fn function_op_cost_sha256_ripemd160() {
1426 let fv = fee_version();
1427 let op = FunctionOp::new_with_round_count(HashFunction::Sha256RipeMD160, 1);
1428 let expected = fv.hashing.sha256_ripe_md160_base + 1 * fv.hashing.sha256_per_block;
1429 assert_eq!(op.cost(fv), expected);
1430 }
1431
1432 #[test]
1433 fn function_op_cost_saturating_mul_does_not_panic_on_large_rounds() {
1434 let fv = fee_version();
1435 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, u32::MAX);
1436 let expected_block_cost = (u32::MAX as u64).saturating_mul(fv.hashing.sha256_per_block);
1439 let expected = fv
1440 .hashing
1441 .single_sha256_base
1442 .saturating_add(expected_block_cost);
1443 assert_eq!(op.cost(fv), expected);
1444 }
1445
1446 #[test]
1447 fn function_op_cost_saturates_to_max_with_extreme_fee_version() {
1448 let mut fv = fee_version().clone();
1451 fv.hashing.sha256_per_block = u64::MAX;
1452 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 2);
1453 assert_eq!(op.cost(&fv), u64::MAX);
1455 }
1456
1457 #[test]
1462 fn operation_cost_calculated_cost_operation_returns_cost() {
1463 let cost = OperationCost {
1464 seek_count: 3,
1465 storage_cost: StorageCost {
1466 added_bytes: 100,
1467 replaced_bytes: 50,
1468 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1469 },
1470 storage_loaded_bytes: 200,
1471 hash_node_calls: 5,
1472 sinsemilla_hash_calls: 0,
1473 };
1474 let op = CalculatedCostOperation(cost.clone());
1475 let result = op.operation_cost().expect("should return Ok");
1476 assert_eq!(result, cost);
1477 }
1478
1479 #[test]
1480 fn operation_cost_grove_operation_returns_error() {
1481 let grove_op = LowLevelDriveOperation::insert_for_known_path_key_element(
1482 vec![vec![1, 2, 3]],
1483 vec![4, 5, 6],
1484 Element::empty_tree(),
1485 );
1486 let result = grove_op.operation_cost();
1487 assert!(result.is_err());
1488 let err_msg = format!("{:?}", result.unwrap_err());
1489 assert!(
1490 err_msg.contains("grove operations must be executed"),
1491 "unexpected error: {}",
1492 err_msg
1493 );
1494 }
1495
1496 #[test]
1497 fn operation_cost_pre_calculated_fee_result_returns_error() {
1498 let fee = FeeResult {
1499 storage_fee: 100,
1500 processing_fee: 200,
1501 ..Default::default()
1502 };
1503 let op = PreCalculatedFeeResult(fee);
1504 let result = op.operation_cost();
1505 assert!(result.is_err());
1506 let err_msg = format!("{:?}", result.unwrap_err());
1507 assert!(
1508 err_msg.contains("pre calculated fees should not be requested"),
1509 "unexpected error: {}",
1510 err_msg
1511 );
1512 }
1513
1514 #[test]
1515 fn operation_cost_function_operation_returns_error() {
1516 let func_op = FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1));
1517 let result = func_op.operation_cost();
1518 assert!(result.is_err());
1519 let err_msg = format!("{:?}", result.unwrap_err());
1520 assert!(
1521 err_msg.contains("function operations should not be requested"),
1522 "unexpected error: {}",
1523 err_msg
1524 );
1525 }
1526
1527 #[test]
1532 fn combine_cost_operations_sums_calculated_costs_only() {
1533 let cost1 = OperationCost {
1534 seek_count: 2,
1535 storage_cost: StorageCost {
1536 added_bytes: 10,
1537 replaced_bytes: 0,
1538 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1539 },
1540 storage_loaded_bytes: 50,
1541 hash_node_calls: 1,
1542 sinsemilla_hash_calls: 0,
1543 };
1544 let cost2 = OperationCost {
1545 seek_count: 3,
1546 storage_cost: StorageCost {
1547 added_bytes: 20,
1548 replaced_bytes: 5,
1549 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1550 },
1551 storage_loaded_bytes: 100,
1552 hash_node_calls: 2,
1553 sinsemilla_hash_calls: 1,
1554 };
1555
1556 let operations = vec![
1557 CalculatedCostOperation(cost1.clone()),
1558 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1560 CalculatedCostOperation(cost2.clone()),
1561 PreCalculatedFeeResult(FeeResult::default()),
1563 ];
1564
1565 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
1566 assert_eq!(combined.seek_count, 2 + 3);
1567 assert_eq!(combined.storage_cost.added_bytes, 10 + 20);
1568 assert_eq!(combined.storage_cost.replaced_bytes, 0 + 5);
1569 assert_eq!(combined.storage_loaded_bytes, 50 + 100);
1570 assert_eq!(combined.hash_node_calls, 1 + 2);
1571 assert_eq!(combined.sinsemilla_hash_calls, 0 + 1);
1572 }
1573
1574 #[test]
1575 fn combine_cost_operations_empty_list_returns_default() {
1576 let combined = LowLevelDriveOperation::combine_cost_operations(&[]);
1577 assert_eq!(combined, OperationCost::default());
1578 }
1579
1580 #[test]
1581 fn combine_cost_operations_no_calculated_costs_returns_default() {
1582 let operations = vec![
1583 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 2)),
1584 PreCalculatedFeeResult(FeeResult {
1585 processing_fee: 999,
1586 ..Default::default()
1587 }),
1588 ];
1589 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
1590 assert_eq!(combined, OperationCost::default());
1591 }
1592
1593 fn make_grove_op(key_byte: u8) -> LowLevelDriveOperation {
1599 LowLevelDriveOperation::insert_for_known_path_key_element(
1600 vec![vec![0]],
1601 vec![key_byte],
1602 Element::new_item(vec![key_byte]),
1603 )
1604 }
1605
1606 fn make_mixed_ops() -> Vec<LowLevelDriveOperation> {
1607 vec![
1608 make_grove_op(1),
1609 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1610 make_grove_op(2),
1611 CalculatedCostOperation(OperationCost::default()),
1612 make_grove_op(3),
1613 ]
1614 }
1615
1616 #[test]
1617 fn grovedb_operations_batch_filters_grove_ops_from_ref() {
1618 let ops = make_mixed_ops();
1619 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
1620 assert_eq!(batch.len(), 3);
1621 }
1622
1623 #[test]
1624 fn grovedb_operations_batch_empty_input() {
1625 let batch = LowLevelDriveOperation::grovedb_operations_batch(&[]);
1626 assert!(batch.is_empty());
1627 }
1628
1629 #[test]
1630 fn grovedb_operations_batch_no_grove_ops() {
1631 let ops = vec![
1632 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1)),
1633 CalculatedCostOperation(OperationCost::default()),
1634 ];
1635 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
1636 assert!(batch.is_empty());
1637 }
1638
1639 #[test]
1640 fn grovedb_operations_batch_consume_filters_grove_ops() {
1641 let ops = make_mixed_ops();
1642 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(ops);
1643 assert_eq!(batch.len(), 3);
1644 }
1645
1646 #[test]
1647 fn grovedb_operations_batch_consume_empty_input() {
1648 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(vec![]);
1649 assert!(batch.is_empty());
1650 }
1651
1652 #[test]
1653 fn grovedb_operations_batch_consume_with_leftovers_partitions_correctly() {
1654 let ops = make_mixed_ops();
1655 let (batch, leftovers) =
1656 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1657 assert_eq!(batch.len(), 3);
1658 assert_eq!(leftovers.len(), 2);
1659
1660 for leftover in &leftovers {
1662 assert!(
1663 !matches!(leftover, GroveOperation(_)),
1664 "leftovers should not contain GroveOperation variants"
1665 );
1666 }
1667 }
1668
1669 #[test]
1670 fn grovedb_operations_batch_consume_with_leftovers_all_grove() {
1671 let ops = vec![make_grove_op(10), make_grove_op(20)];
1672 let (batch, leftovers) =
1673 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1674 assert_eq!(batch.len(), 2);
1675 assert!(leftovers.is_empty());
1676 }
1677
1678 #[test]
1679 fn grovedb_operations_batch_consume_with_leftovers_no_grove() {
1680 let ops = vec![
1681 CalculatedCostOperation(OperationCost::default()),
1682 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
1683 ];
1684 let (batch, leftovers) =
1685 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
1686 assert!(batch.is_empty());
1687 assert_eq!(leftovers.len(), 2);
1688 }
1689
1690 #[test]
1691 fn grovedb_operations_batch_consume_with_leftovers_empty() {
1692 let (batch, leftovers) =
1693 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(vec![]);
1694 assert!(batch.is_empty());
1695 assert!(leftovers.is_empty());
1696 }
1697
1698 #[test]
1703 fn ephemeral_cost_zero_operation() {
1704 let fv = fee_version();
1705 let cost = OperationCost::default();
1706 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1707 assert_eq!(result, 0);
1708 }
1709
1710 #[test]
1711 fn ephemeral_cost_seek_only() {
1712 let fv = fee_version();
1713 let cost = OperationCost {
1714 seek_count: 5,
1715 storage_cost: StorageCost::default(),
1716 storage_loaded_bytes: 0,
1717 hash_node_calls: 0,
1718 sinsemilla_hash_calls: 0,
1719 };
1720 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1721 let expected = 5u64 * fv.storage.storage_seek_cost;
1722 assert_eq!(result, expected);
1723 }
1724
1725 #[test]
1726 fn ephemeral_cost_storage_added_bytes() {
1727 let fv = fee_version();
1728 let cost = OperationCost {
1729 seek_count: 0,
1730 storage_cost: StorageCost {
1731 added_bytes: 100,
1732 replaced_bytes: 0,
1733 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1734 },
1735 storage_loaded_bytes: 0,
1736 hash_node_calls: 0,
1737 sinsemilla_hash_calls: 0,
1738 };
1739 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1740 let expected = 100u64 * fv.storage.storage_processing_credit_per_byte;
1741 assert_eq!(result, expected);
1742 }
1743
1744 #[test]
1745 fn ephemeral_cost_storage_replaced_bytes() {
1746 let fv = fee_version();
1747 let cost = OperationCost {
1748 seek_count: 0,
1749 storage_cost: StorageCost {
1750 added_bytes: 0,
1751 replaced_bytes: 50,
1752 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1753 },
1754 storage_loaded_bytes: 0,
1755 hash_node_calls: 0,
1756 sinsemilla_hash_calls: 0,
1757 };
1758 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1759 let expected = 50u64 * fv.storage.storage_processing_credit_per_byte;
1760 assert_eq!(result, expected);
1761 }
1762
1763 #[test]
1764 fn ephemeral_cost_storage_removed_bytes_basic() {
1765 let fv = fee_version();
1766 let cost = OperationCost {
1767 seek_count: 0,
1768 storage_cost: StorageCost {
1769 added_bytes: 0,
1770 replaced_bytes: 0,
1771 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(75),
1772 },
1773 storage_loaded_bytes: 0,
1774 hash_node_calls: 0,
1775 sinsemilla_hash_calls: 0,
1776 };
1777 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1778 let expected = 75u64 * fv.storage.storage_processing_credit_per_byte;
1779 assert_eq!(result, expected);
1780 }
1781
1782 #[test]
1783 fn ephemeral_cost_loaded_bytes() {
1784 let fv = fee_version();
1785 let cost = OperationCost {
1786 seek_count: 0,
1787 storage_cost: StorageCost::default(),
1788 storage_loaded_bytes: 300,
1789 hash_node_calls: 0,
1790 sinsemilla_hash_calls: 0,
1791 };
1792 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1793 let expected = 300u64 * fv.storage.storage_load_credit_per_byte;
1794 assert_eq!(result, expected);
1795 }
1796
1797 #[test]
1798 fn ephemeral_cost_hash_node_calls() {
1799 let fv = fee_version();
1800 let cost = OperationCost {
1801 seek_count: 0,
1802 storage_cost: StorageCost::default(),
1803 storage_loaded_bytes: 0,
1804 hash_node_calls: 10,
1805 sinsemilla_hash_calls: 0,
1806 };
1807 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1808 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
1809 let expected = blake3_total * 10;
1810 assert_eq!(result, expected);
1811 }
1812
1813 #[test]
1814 fn ephemeral_cost_sinsemilla_hash_calls() {
1815 let fv = fee_version();
1816 let cost = OperationCost {
1817 seek_count: 0,
1818 storage_cost: StorageCost::default(),
1819 storage_loaded_bytes: 0,
1820 hash_node_calls: 0,
1821 sinsemilla_hash_calls: 3,
1822 };
1823 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1824 let expected = fv.hashing.sinsemilla_base * 3;
1825 assert_eq!(result, expected);
1826 }
1827
1828 #[test]
1829 fn ephemeral_cost_all_components_combined() {
1830 let fv = fee_version();
1831 let cost = OperationCost {
1832 seek_count: 2,
1833 storage_cost: StorageCost {
1834 added_bytes: 10,
1835 replaced_bytes: 20,
1836 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(30),
1837 },
1838 storage_loaded_bytes: 40,
1839 hash_node_calls: 5,
1840 sinsemilla_hash_calls: 1,
1841 };
1842 let result = cost.ephemeral_cost(fv).expect("should not overflow");
1843
1844 let seek_cost = 2u64 * fv.storage.storage_seek_cost;
1845 let processing_per_byte = fv.storage.storage_processing_credit_per_byte;
1846 let added_cost = 10u64 * processing_per_byte;
1847 let replaced_cost = 20u64 * processing_per_byte;
1848 let removed_cost = 30u64 * processing_per_byte;
1849 let loaded_cost = 40u64 * fv.storage.storage_load_credit_per_byte;
1850 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
1851 let hash_cost = blake3_total * 5;
1852 let sinsemilla_cost = fv.hashing.sinsemilla_base * 1;
1853
1854 let expected = seek_cost
1855 + added_cost
1856 + replaced_cost
1857 + loaded_cost
1858 + removed_cost
1859 + hash_cost
1860 + sinsemilla_cost;
1861 assert_eq!(result, expected);
1862 }
1863
1864 #[test]
1865 fn ephemeral_cost_overflow_seek_cost() {
1866 let fv = &FeeVersion {
1867 storage: FeeStorageVersion {
1868 storage_seek_cost: u64::MAX,
1869 ..fee_version().storage.clone()
1870 },
1871 ..fee_version().clone()
1872 };
1873 let cost = OperationCost {
1874 seek_count: 2, storage_cost: StorageCost::default(),
1876 storage_loaded_bytes: 0,
1877 hash_node_calls: 0,
1878 sinsemilla_hash_calls: 0,
1879 };
1880 let result = cost.ephemeral_cost(fv);
1881 assert!(result.is_err(), "expected overflow error for seek cost");
1882 }
1883
1884 #[test]
1885 fn ephemeral_cost_overflow_storage_written_bytes() {
1886 let fv = &FeeVersion {
1887 storage: FeeStorageVersion {
1888 storage_processing_credit_per_byte: u64::MAX,
1889 ..fee_version().storage.clone()
1890 },
1891 ..fee_version().clone()
1892 };
1893 let cost = OperationCost {
1894 seek_count: 0,
1895 storage_cost: StorageCost {
1896 added_bytes: 2, replaced_bytes: 0,
1898 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
1899 },
1900 storage_loaded_bytes: 0,
1901 hash_node_calls: 0,
1902 sinsemilla_hash_calls: 0,
1903 };
1904 let result = cost.ephemeral_cost(fv);
1905 assert!(
1906 result.is_err(),
1907 "expected overflow error for storage written bytes"
1908 );
1909 }
1910
1911 #[test]
1912 fn ephemeral_cost_overflow_loaded_bytes() {
1913 let fv = &FeeVersion {
1914 storage: FeeStorageVersion {
1915 storage_load_credit_per_byte: u64::MAX,
1916 ..fee_version().storage.clone()
1917 },
1918 ..fee_version().clone()
1919 };
1920 let cost = OperationCost {
1921 seek_count: 0,
1922 storage_cost: StorageCost::default(),
1923 storage_loaded_bytes: 2, hash_node_calls: 0,
1925 sinsemilla_hash_calls: 0,
1926 };
1927 let result = cost.ephemeral_cost(fv);
1928 assert!(
1929 result.is_err(),
1930 "expected overflow error for loaded bytes cost"
1931 );
1932 }
1933
1934 #[test]
1940 fn empty_tree_operation_for_known_path_key_provable_sum_tree() {
1941 use grovedb::batch::GroveOp;
1942
1943 let op = TreeType::ProvableSumTree
1944 .empty_tree_operation_for_known_path_key(vec![b"root".to_vec()], b"k".to_vec(), None)
1945 .expect("empty_tree_operation_for_known_path_key");
1946
1947 match op {
1948 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
1949 GroveOp::InsertOrReplace { element } => assert!(
1950 matches!(element, Element::ProvableSumTree(..)),
1951 "expected ProvableSumTree element, got: {:?}",
1952 element
1953 ),
1954 other => panic!("expected GroveOp::InsertOrReplace, got: {:?}", other),
1955 },
1956 other => panic!("expected GroveOperation, got: {:?}", other),
1957 }
1958 }
1959
1960 #[test]
1961 fn ephemeral_cost_overflow_in_addition_chain() {
1962 let fv = fee_version();
1964 let cost = OperationCost {
1965 seek_count: u32::MAX,
1966 storage_cost: StorageCost {
1967 added_bytes: u32::MAX,
1968 replaced_bytes: u32::MAX,
1969 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(u32::MAX),
1970 },
1971 storage_loaded_bytes: u64::MAX,
1972 hash_node_calls: u32::MAX,
1973 sinsemilla_hash_calls: u32::MAX,
1974 };
1975 let result = cost.ephemeral_cost(fv);
1976 assert!(
1977 result.is_err(),
1978 "expected overflow error when summing large components"
1979 );
1980 }
1981}