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::GroveOp;
11use grovedb::batch::KeyInfoPath;
12use grovedb::element::reference_path::ReferencePathType;
13use grovedb::element::IndexAxis;
14use grovedb::element::MaxReferenceHop;
15use grovedb::{batch::QualifiedGroveDbOp, Element, ElementFlags, TreeType};
16use grovedb_costs::OperationCost;
17
18use crate::error::drive::DriveError;
19use crate::error::fee::FeeError;
20use crate::error::Error;
21use crate::fees::get_overflow_error;
22use crate::fees::op::LowLevelDriveOperation::{
23 CalculatedCostOperation, CalculatedEphemeralCostOperation, EphemeralGroveOperation,
24 FunctionOperation, GroveOperation, PreCalculatedFeeResult,
25};
26use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods;
27use crate::util::storage_flags::StorageFlags;
28use dpp::block::epoch::Epoch;
29use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
30use dpp::fee::fee_result::refunds::FeeRefunds;
31use dpp::fee::fee_result::FeeResult;
32use dpp::fee::Credits;
33use platform_version::version::fee::FeeVersion;
34
35#[derive(Debug, Enum)]
37pub enum BaseOp {
38 Stop,
40 Add,
42 Mul,
44 Sub,
46 Div,
48 Sdiv,
50 Mod,
52 Smod,
54 Addmod,
56 Mulmod,
58 Signextend,
60 Lt,
62 Gt,
64 Slt,
66 Sgt,
68 Eq,
70 Iszero,
72 And,
74 Or,
76 Xor,
78 Not,
80 Byte,
82}
83
84impl BaseOp {
85 pub fn cost(&self) -> u64 {
87 match self {
88 BaseOp::Stop => 0,
89 BaseOp::Add => 12,
90 BaseOp::Mul => 20,
91 BaseOp::Sub => 12,
92 BaseOp::Div => 20,
93 BaseOp::Sdiv => 20,
94 BaseOp::Mod => 20,
95 BaseOp::Smod => 20,
96 BaseOp::Addmod => 32,
97 BaseOp::Mulmod => 32,
98 BaseOp::Signextend => 20,
99 BaseOp::Lt => 12,
100 BaseOp::Gt => 12,
101 BaseOp::Slt => 12,
102 BaseOp::Sgt => 12,
103 BaseOp::Eq => 12,
104 BaseOp::Iszero => 12,
105 BaseOp::And => 12,
106 BaseOp::Or => 12,
107 BaseOp::Xor => 12,
108 BaseOp::Not => 12,
109 BaseOp::Byte => 12,
110 }
111 }
112}
113
114#[derive(Debug, Enum, PartialEq, Eq)]
116pub enum HashFunction {
117 Sha256RipeMD160,
119 Sha256,
121 Sha256_2,
123 Blake3,
125}
126
127impl HashFunction {
128 fn block_size(&self) -> u16 {
129 match self {
130 HashFunction::Sha256 => 64,
131 HashFunction::Sha256_2 => 64,
132 HashFunction::Blake3 => 64,
133 HashFunction::Sha256RipeMD160 => 64,
134 }
135 }
136
137 fn rounds(&self) -> u16 {
138 match self {
139 HashFunction::Sha256 => 1,
140 HashFunction::Sha256_2 => 2,
141 HashFunction::Blake3 => 1,
142 HashFunction::Sha256RipeMD160 => 1,
143 }
144 }
145
146 fn block_cost(&self, fee_version: &FeeVersion) -> u64 {
147 match self {
148 HashFunction::Sha256 => fee_version.hashing.sha256_per_block,
149 HashFunction::Sha256_2 => fee_version.hashing.sha256_per_block,
150 HashFunction::Blake3 => fee_version.hashing.blake3_per_block,
151 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_per_block,
152 }
153 }
154
155 fn base_cost(&self, fee_version: &FeeVersion) -> u64 {
156 match self {
157 HashFunction::Sha256 => fee_version.hashing.single_sha256_base,
158 HashFunction::Sha256_2 => fee_version.hashing.single_sha256_base,
161 HashFunction::Blake3 => fee_version.hashing.blake3_base,
162 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_ripe_md160_base,
163 }
164 }
165}
166
167#[derive(Debug, PartialEq, Eq)]
169pub struct FunctionOp {
170 pub(crate) hash: HashFunction,
172 pub(crate) rounds: u32,
174}
175
176impl FunctionOp {
177 fn cost(&self, fee_version: &FeeVersion) -> Credits {
179 let block_cost = (self.rounds as u64).saturating_mul(self.hash.block_cost(fee_version));
180 self.hash.base_cost(fee_version).saturating_add(block_cost)
181 }
182
183 pub fn new_with_round_count(hash: HashFunction, rounds: u32) -> Self {
186 FunctionOp { hash, rounds }
187 }
188
189 pub fn new_with_byte_count(hash: HashFunction, byte_count: u16) -> Self {
192 let blocks = byte_count / hash.block_size() + 1;
193 let rounds = blocks + hash.rounds() - 1;
194 FunctionOp {
195 hash,
196 rounds: rounds as u32,
197 }
198 }
199}
200
201#[allow(clippy::large_enum_variant)]
206#[derive(Debug, Eq, PartialEq)]
207pub enum LowLevelDriveOperation {
208 GroveOperation(QualifiedGroveDbOp),
210 EphemeralGroveOperation(QualifiedGroveDbOp),
220 FunctionOperation(FunctionOp),
222 CalculatedCostOperation(OperationCost),
224 CalculatedEphemeralCostOperation(OperationCost),
228 PreCalculatedFeeResult(FeeResult),
230}
231
232const INDEXED_INNER_UNWRAPPABLE: &str =
252 "an indexed tree cannot be wrapped in NonCounted / NotSummed / NotCountedOrSummed: the \
253 wrapper suppresses the subtree's contribution to its parent's aggregate, but an indexed \
254 primary commits its aggregate (and the derived secondary root keys) through that very \
255 parent element. A ranked index's terminal property-name tree therefore cannot live inside \
256 an aggregating value tree — i.e. a ranked compound index [a, b] cannot coexist with a \
257 countable/summable index terminating at [a]; contracts declaring that pair are rejected \
258 at parse time.";
259
260impl LowLevelDriveOperation {
261 pub fn consume_to_fees_v0(
264 drive_operations: Vec<LowLevelDriveOperation>,
265 epoch: &Epoch,
266 epochs_per_era: u16,
267 fee_version: &FeeVersion,
268 previous_fee_versions: Option<&CachedEpochIndexFeeVersions>,
269 ) -> Result<Vec<FeeResult>, Error> {
270 drive_operations
271 .into_iter()
272 .map(|operation| match operation {
273 PreCalculatedFeeResult(f) => Ok(f),
274 FunctionOperation(op) => Ok(FeeResult {
275 processing_fee: op.cost(fee_version),
276 ..Default::default()
277 }),
278 CalculatedEphemeralCostOperation(cost) => {
279 let ephemeral_bytes_fee = (cost.storage_cost.added_bytes as u64)
287 .checked_mul(
288 fee_version
289 .storage
290 .ttl_ephemeral_disk_usage_credit_per_byte,
291 )
292 .ok_or(Error::Fee(FeeError::Overflow(
293 "overflow pricing ephemeral bytes",
294 )))?;
295 let processing_fee = cost
296 .ephemeral_cost(fee_version)?
297 .checked_add(ephemeral_bytes_fee)
298 .ok_or(Error::Fee(FeeError::Overflow(
299 "overflow adding ephemeral bytes fee",
300 )))?;
301 let removed_bytes_from_system = match cost.storage_cost.removed_bytes {
302 NoStorageRemoval => 0,
303 BasicStorageRemoval(amount) => amount,
304 SectionedStorageRemoval(_) => {
305 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
306 "TTL'd subtrees carry no storage flags, so an ephemeral \
307 batch cannot produce sectioned (refundable) removal",
308 )))
309 }
310 };
311 Ok(FeeResult {
312 storage_fee: 0,
313 processing_fee,
314 fee_refunds: FeeRefunds::default(),
315 removed_bytes_from_system,
316 })
317 }
318 _ => {
319 let cost = operation.operation_cost()?;
320 let storage_fee = cost.storage_cost.added_bytes as u64 * fee_version.storage.storage_disk_usage_credit_per_byte;
323 let processing_fee = cost.ephemeral_cost(fee_version)?;
324 let (fee_refunds, removed_bytes_from_system) =
325 match cost.storage_cost.removed_bytes {
326 NoStorageRemoval => (FeeRefunds::default(), 0),
327 BasicStorageRemoval(amount) => {
328 (FeeRefunds::default(), amount)
330 }
331 SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => {
332
333 let system_amount = removal_per_epoch_by_identifier
334 .remove(&Identifier::default())
335 .map_or(0, |a| a.values().sum());
336 if fee_version.fee_version_number == 1 {
337 (
338 FeeRefunds::from_storage_removal(
339 removal_per_epoch_by_identifier,
340 epoch.index,
341 epochs_per_era,
342 &BTreeMap::default(),
343 )?,
344 system_amount,
345 )
346 } else {
347 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")))?;
348 (
349 FeeRefunds::from_storage_removal(
350 removal_per_epoch_by_identifier,
351 epoch.index,
352 epochs_per_era,
353 previous_fee_versions,
354 )?,
355 system_amount,
356 )
357 }
358 }
359 };
360 Ok(FeeResult {
361 storage_fee,
362 processing_fee,
363 fee_refunds,
364 removed_bytes_from_system,
365 })
366 }
367 })
368 .collect()
369 }
370
371 pub fn operation_cost(self) -> Result<OperationCost, Error> {
373 match self {
374 GroveOperation(_) | EphemeralGroveOperation(_) => {
375 Err(Error::Drive(DriveError::CorruptedCodeExecution(
376 "grove operations must be executed, not directly transformed to costs",
377 )))
378 }
379 CalculatedCostOperation(c) | CalculatedEphemeralCostOperation(c) => Ok(c),
380 PreCalculatedFeeResult(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
381 "pre calculated fees should not be requested by operation costs",
382 ))),
383 FunctionOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
384 "function operations should not be requested by operation costs",
385 ))),
386 }
387 }
388
389 pub fn combine_cost_operations(operations: &[LowLevelDriveOperation]) -> OperationCost {
391 let mut cost = OperationCost::default();
392 operations.iter().for_each(|op| {
393 if let CalculatedCostOperation(operation_cost) = op {
394 cost += operation_cost.clone()
395 }
396 });
397 cost
398 }
399
400 pub fn grovedb_operations_batch(
402 insert_operations: &[LowLevelDriveOperation],
403 ) -> GroveDbOpBatch {
404 let operations = insert_operations
405 .iter()
406 .filter_map(|op| match op {
407 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op) => {
408 Some(grovedb_op.clone())
409 }
410 _ => None,
411 })
412 .collect();
413 GroveDbOpBatch::from_operations(operations)
414 }
415
416 pub fn grovedb_operations_batch_consume(
418 insert_operations: Vec<LowLevelDriveOperation>,
419 ) -> GroveDbOpBatch {
420 let operations = insert_operations
421 .into_iter()
422 .filter_map(|op| match op {
423 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op) => {
424 Some(grovedb_op)
425 }
426 _ => None,
427 })
428 .collect();
429 GroveDbOpBatch::from_operations(operations)
430 }
431
432 pub fn grovedb_operations_batch_consume_with_leftovers(
439 insert_operations: Vec<LowLevelDriveOperation>,
440 ) -> (GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
441 let mut grove_operations = vec![];
442 let mut other_operations = vec![];
443 for op in insert_operations {
444 match op {
445 GroveOperation(grovedb_op) => grove_operations.push(grovedb_op),
446 other => other_operations.push(other),
447 }
448 }
449 (
450 GroveDbOpBatch::from_operations(grove_operations),
451 other_operations,
452 )
453 }
454
455 pub fn grovedb_operations_batch_consume_split_ephemeral(
460 insert_operations: Vec<LowLevelDriveOperation>,
461 ) -> (GroveDbOpBatch, GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
462 let mut grove_operations = vec![];
463 let mut ephemeral_operations = vec![];
464 let mut other_operations = vec![];
465 for op in insert_operations {
466 match op {
467 GroveOperation(grovedb_op) => grove_operations.push(grovedb_op),
468 EphemeralGroveOperation(grovedb_op) => ephemeral_operations.push(grovedb_op),
469 other => other_operations.push(other),
470 }
471 }
472 (
473 GroveDbOpBatch::from_operations(grove_operations),
474 GroveDbOpBatch::from_operations(ephemeral_operations),
475 other_operations,
476 )
477 }
478
479 pub fn retag_ephemeral(self) -> LowLevelDriveOperation {
485 match self {
486 GroveOperation(mut grovedb_op) => {
487 match &mut grovedb_op.op {
495 GroveOp::InsertWithKnownToNotAlreadyExist { element }
496 | GroveOp::InsertIfNotExists { element, .. }
497 | GroveOp::InsertOrReplace { element }
498 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element }
499 | GroveOp::Replace { element }
500 | GroveOp::ReplaceDontCheckForBackwardsReferences { element }
501 | GroveOp::Patch { element, .. }
502 | GroveOp::PatchDontCheckForBackwardsReferences { element, .. } => {
503 element.set_flags(None)
504 }
505 GroveOp::RefreshReference { flags, .. } => *flags = None,
506 _ => {}
507 }
508 EphemeralGroveOperation(grovedb_op)
509 }
510 CalculatedCostOperation(cost) => CalculatedEphemeralCostOperation(cost),
511 other => other,
512 }
513 }
514
515 pub fn grovedb_operations_consume(
517 insert_operations: Vec<LowLevelDriveOperation>,
518 ) -> Vec<QualifiedGroveDbOp> {
519 insert_operations
520 .into_iter()
521 .filter_map(|op| match op {
522 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op) => {
523 Some(grovedb_op)
524 }
525 _ => None,
526 })
527 .collect()
528 }
529
530 pub fn for_known_path_key_empty_tree(
532 path: Vec<Vec<u8>>,
533 key: Vec<u8>,
534 storage_flags: Option<&StorageFlags>,
535 ) -> Self {
536 let tree = match storage_flags {
537 Some(storage_flags) => {
538 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
539 }
540 None => Element::empty_tree(),
541 };
542
543 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
544 }
545
546 pub fn for_known_path_key_empty_sum_tree(
548 path: Vec<Vec<u8>>,
549 key: Vec<u8>,
550 storage_flags: Option<&StorageFlags>,
551 ) -> Self {
552 let tree = match storage_flags {
553 Some(storage_flags) => {
554 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
555 }
556 None => Element::empty_sum_tree(),
557 };
558
559 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
560 }
561
562 pub fn for_known_path_key_empty_big_sum_tree(
564 path: Vec<Vec<u8>>,
565 key: Vec<u8>,
566 storage_flags: Option<&StorageFlags>,
567 ) -> Self {
568 let tree = match storage_flags {
569 Some(storage_flags) => {
570 Element::new_big_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
571 }
572 None => Element::empty_big_sum_tree(),
573 };
574
575 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
576 }
577
578 pub fn for_known_path_key_empty_count_tree(
580 path: Vec<Vec<u8>>,
581 key: Vec<u8>,
582 storage_flags: Option<&StorageFlags>,
583 ) -> Self {
584 let tree = match storage_flags {
585 Some(storage_flags) => {
586 Element::new_count_tree_with_flags(None, storage_flags.to_some_element_flags())
587 }
588 None => Element::empty_count_tree(),
589 };
590
591 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
592 }
593
594 pub fn for_known_path_key_empty_count_sum_tree(
596 path: Vec<Vec<u8>>,
597 key: Vec<u8>,
598 storage_flags: Option<&StorageFlags>,
599 ) -> Self {
600 let tree = match storage_flags {
601 Some(storage_flags) => {
602 Element::new_count_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
603 }
604 None => Element::new_count_sum_tree(None),
605 };
606
607 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
608 }
609
610 pub fn for_known_path_key_empty_non_counted_normal_tree(
618 path: Vec<Vec<u8>>,
619 key: Vec<u8>,
620 storage_flags: Option<&StorageFlags>,
621 ) -> Self {
622 Self::for_known_path_key_empty_non_counted_tree(
623 path,
624 key,
625 TreeType::NormalTree,
626 storage_flags,
627 )
628 .expect("NormalTree NonCounted wrapping never fails")
629 }
630
631 pub fn for_known_path_key_empty_non_counted_tree(
648 path: Vec<Vec<u8>>,
649 key: Vec<u8>,
650 tree_type: TreeType,
651 storage_flags: Option<&StorageFlags>,
652 ) -> Result<Self, Error> {
653 let element_flags = storage_flags.map(|s| s.to_element_flags());
660 let inner = match tree_type {
661 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
662 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
663 TreeType::ProvableCountTree => {
664 Element::empty_provable_count_tree_with_flags(element_flags)
665 }
666 TreeType::ProvableSumIndexedTree
667 | TreeType::ProvableCountIndexedTree
668 | TreeType::ProvableCountProvableSumIndexedTree => {
669 return Err(Error::Drive(DriveError::NotSupported(
670 INDEXED_INNER_UNWRAPPABLE,
671 )));
672 }
673 _ => {
674 return Err(Error::Drive(DriveError::NotSupported(
675 "NonCounted-wrapping is only supported for NormalTree, CountTree, and \
676 ProvableCountTree. For sum-bearing continuations under a sum or \
677 count+sum parent, use `for_known_path_key_empty_not_summed_tree` or \
678 `for_known_path_key_empty_not_counted_or_summed_tree` instead.",
679 )));
680 }
681 };
682 let tree = Element::new_non_counted(inner)?;
694 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
695 path, key, tree,
696 ))
697 }
698
699 pub fn for_known_path_key_empty_not_summed_tree(
708 path: Vec<Vec<u8>>,
709 key: Vec<u8>,
710 tree_type: TreeType,
711 storage_flags: Option<&StorageFlags>,
712 ) -> Result<Self, Error> {
713 let element_flags = storage_flags.map(|s| s.to_element_flags());
714 let inner = match tree_type {
715 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
716 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
717 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
718 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
719 TreeType::ProvableCountSumTree => {
720 Element::empty_provable_count_sum_tree_with_flags(element_flags)
721 }
722 TreeType::ProvableCountProvableSumTree => {
723 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
724 }
725 TreeType::ProvableSumIndexedTree
726 | TreeType::ProvableCountIndexedTree
727 | TreeType::ProvableCountProvableSumIndexedTree => {
728 return Err(Error::Drive(DriveError::NotSupported(
729 INDEXED_INNER_UNWRAPPABLE,
730 )));
731 }
732 _ => {
733 return Err(Error::Drive(DriveError::NotSupported(
734 "NotSummed-wrapping is only supported for the six sum-bearing tree \
735 variants (SumTree, BigSumTree, ProvableSumTree, CountSumTree, \
736 ProvableCountSumTree, ProvableCountProvableSumTree).",
737 )));
738 }
739 };
740 let tree = Element::new_not_summed(inner).map_err(|_| {
741 Error::Drive(DriveError::NotSupported(
742 "Element::new_not_summed rejected the inner tree (unreachable given the \
743 match above).",
744 ))
745 })?;
746 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
747 path, key, tree,
748 ))
749 }
750
751 pub fn wrap_in_non_aggregated_for_parent_tree_type(
780 path: Vec<Vec<u8>>,
781 key: Vec<u8>,
782 aggregating_parent_tree_type: TreeType,
783 inner_tree_type: TreeType,
784 storage_flags: Option<&StorageFlags>,
785 ) -> Result<Self, Error> {
786 match aggregating_parent_tree_type {
787 TreeType::CountTree
796 | TreeType::ProvableCountTree
797 | TreeType::ProvableCountIndexedTree => {
798 Self::for_known_path_key_empty_non_counted_tree(
799 path,
800 key,
801 inner_tree_type,
802 storage_flags,
803 )
804 }
805 TreeType::SumTree
809 | TreeType::BigSumTree
810 | TreeType::ProvableSumTree
811 | TreeType::ProvableSumIndexedTree => Self::for_known_path_key_empty_not_summed_tree(
812 path,
813 key,
814 inner_tree_type,
815 storage_flags,
816 ),
817 TreeType::CountSumTree
820 | TreeType::ProvableCountSumTree
821 | TreeType::ProvableCountProvableSumTree
822 | TreeType::ProvableCountProvableSumIndexedTree => {
823 Self::for_known_path_key_empty_not_counted_or_summed_tree(
824 path,
825 key,
826 inner_tree_type,
827 storage_flags,
828 )
829 }
830 _ => Err(Error::Drive(DriveError::NotSupported(
831 "wrap_in_non_aggregated_for_parent_tree_type called with a non-aggregating \
832 parent tree type — caller should use the unwrapped \
833 `empty_tree_operation_for_known_path_key` path instead.",
834 ))),
835 }
836 }
837
838 pub fn for_known_path_key_empty_not_counted_or_summed_tree(
846 path: Vec<Vec<u8>>,
847 key: Vec<u8>,
848 tree_type: TreeType,
849 storage_flags: Option<&StorageFlags>,
850 ) -> Result<Self, Error> {
851 let element_flags = storage_flags.map(|s| s.to_element_flags());
852 let inner = match tree_type {
853 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
854 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
855 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
856 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
857 TreeType::ProvableCountSumTree => {
858 Element::empty_provable_count_sum_tree_with_flags(element_flags)
859 }
860 TreeType::ProvableCountProvableSumTree => {
861 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
862 }
863 TreeType::ProvableSumIndexedTree
864 | TreeType::ProvableCountIndexedTree
865 | TreeType::ProvableCountProvableSumIndexedTree => {
866 return Err(Error::Drive(DriveError::NotSupported(
867 INDEXED_INNER_UNWRAPPABLE,
868 )));
869 }
870 _ => {
871 return Err(Error::Drive(DriveError::NotSupported(
872 "NotCountedOrSummed-wrapping is only supported for the six sum-bearing \
873 tree variants — see `for_known_path_key_empty_not_summed_tree`.",
874 )));
875 }
876 };
877 let tree = Element::new_not_counted_or_summed(inner).map_err(|_| {
878 Error::Drive(DriveError::NotSupported(
879 "Element::new_not_counted_or_summed rejected the inner tree (unreachable \
880 given the match above).",
881 ))
882 })?;
883 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
884 path, key, tree,
885 ))
886 }
887
888 pub fn for_known_path_key_empty_tree_contributing_zero_to_parent(
927 path: Vec<Vec<u8>>,
928 key: Vec<u8>,
929 aggregating_parent_tree_type: TreeType,
930 inner_tree_type: TreeType,
931 storage_flags: Option<&StorageFlags>,
932 ) -> Result<Self, Error> {
933 if matches!(
942 inner_tree_type,
943 TreeType::ProvableSumIndexedTree
944 | TreeType::ProvableCountIndexedTree
945 | TreeType::ProvableCountProvableSumIndexedTree
946 ) {
947 return Err(Error::Drive(DriveError::NotSupported(
948 INDEXED_INNER_UNWRAPPABLE,
949 )));
950 }
951 let inner_is_sum_bearing = matches!(
952 inner_tree_type,
953 TreeType::SumTree
954 | TreeType::BigSumTree
955 | TreeType::ProvableSumTree
956 | TreeType::CountSumTree
957 | TreeType::ProvableCountSumTree
958 | TreeType::ProvableCountProvableSumTree
959 );
960 match aggregating_parent_tree_type {
961 TreeType::CountTree => Self::for_known_path_key_empty_non_counted_any_tree(
962 path,
963 key,
964 inner_tree_type,
965 storage_flags,
966 ),
967 TreeType::CountSumTree => {
968 if inner_is_sum_bearing {
969 Self::for_known_path_key_empty_not_counted_or_summed_tree(
970 path,
971 key,
972 inner_tree_type,
973 storage_flags,
974 )
975 } else {
976 Self::for_known_path_key_empty_non_counted_any_tree(
977 path,
978 key,
979 inner_tree_type,
980 storage_flags,
981 )
982 }
983 }
984 TreeType::SumTree | TreeType::BigSumTree | TreeType::ProvableSumTree => {
985 if inner_is_sum_bearing {
986 Self::for_known_path_key_empty_not_summed_tree(
987 path,
988 key,
989 inner_tree_type,
990 storage_flags,
991 )
992 } else {
993 inner_tree_type.empty_tree_operation_for_known_path_key(
994 path,
995 key,
996 storage_flags,
997 )
998 }
999 }
1000 TreeType::ProvableCountIndexedTree
1010 | TreeType::ProvableSumIndexedTree
1011 | TreeType::ProvableCountProvableSumIndexedTree => {
1012 Err(Error::Drive(DriveError::NotSupported(
1013 "indexed trees are property-name trees, never value trees, so they cannot \
1014 host zero-contributing continuation children — see \
1015 crate::drive::document::ranked_index_tree_type.",
1016 )))
1017 }
1018 TreeType::ProvableCountTree
1019 | TreeType::ProvableCountSumTree
1020 | TreeType::ProvableCountProvableSumTree => {
1021 Err(Error::Drive(DriveError::NotSupported(
1022 "provable count-bearing parents cannot host zero-contributing children — \
1023 grovedb commits their count into every node hash and rejects NonCounted / \
1024 NotCountedOrSummed children; the index walker must demote such value trees \
1025 to CountSumTree before hanging continuations under them (see \
1026 index_level_tree_types_with_continuation_demotion).",
1027 )))
1028 }
1029 _ => Err(Error::Drive(DriveError::NotSupported(
1030 "for_known_path_key_empty_tree_contributing_zero_to_parent called with a \
1031 non-aggregating parent tree type — caller should use the unwrapped \
1032 `empty_tree_operation_for_known_path_key` path instead.",
1033 ))),
1034 }
1035 }
1036
1037 pub fn for_known_path_key_empty_non_counted_any_tree(
1054 path: Vec<Vec<u8>>,
1055 key: Vec<u8>,
1056 tree_type: TreeType,
1057 storage_flags: Option<&StorageFlags>,
1058 ) -> Result<Self, Error> {
1059 let element_flags = storage_flags.map(|s| s.to_element_flags());
1060 let inner = match tree_type {
1061 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1062 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1063 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1064 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1065 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1066 TreeType::ProvableCountTree => {
1067 Element::empty_provable_count_tree_with_flags(element_flags)
1068 }
1069 TreeType::ProvableCountSumTree => {
1070 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1071 }
1072 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1073 TreeType::ProvableCountProvableSumTree => {
1074 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1075 }
1076 TreeType::ProvableSumIndexedTree
1077 | TreeType::ProvableCountIndexedTree
1078 | TreeType::ProvableCountProvableSumIndexedTree => {
1079 return Err(Error::Drive(DriveError::NotSupported(
1080 INDEXED_INNER_UNWRAPPABLE,
1081 )));
1082 }
1083 _ => {
1084 return Err(Error::Drive(DriveError::NotSupported(
1085 "NonCounted-wrapping is only supported for the nine standard merk tree \
1086 variants; special trees (commitment / MMR / bulk-append / dense) are \
1087 never index continuation trees.",
1088 )));
1089 }
1090 };
1091 let tree = Element::new_non_counted(inner)?;
1092 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1093 path, key, tree,
1094 ))
1095 }
1096
1097 pub fn for_known_path_key_empty_provable_count_tree(
1099 path: Vec<Vec<u8>>,
1100 key: Vec<u8>,
1101 storage_flags: Option<&StorageFlags>,
1102 ) -> Self {
1103 let tree = match storage_flags {
1104 Some(storage_flags) => Element::new_provable_count_tree_with_flags(
1105 None,
1106 storage_flags.to_some_element_flags(),
1107 ),
1108 None => Element::empty_provable_count_tree(),
1109 };
1110
1111 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1112 }
1113
1114 pub fn for_known_path_key_empty_provable_sum_tree(
1124 path: Vec<Vec<u8>>,
1125 key: Vec<u8>,
1126 storage_flags: Option<&StorageFlags>,
1127 ) -> Self {
1128 let tree = match storage_flags {
1129 Some(storage_flags) => Element::new_provable_sum_tree_with_flags(
1130 None,
1131 storage_flags.to_some_element_flags(),
1132 ),
1133 None => Element::empty_provable_sum_tree(),
1134 };
1135
1136 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1137 }
1138
1139 pub fn for_known_path_key_empty_provable_count_sum_tree(
1148 path: Vec<Vec<u8>>,
1149 key: Vec<u8>,
1150 storage_flags: Option<&StorageFlags>,
1151 ) -> Self {
1152 let tree = match storage_flags {
1153 Some(storage_flags) => Element::new_provable_count_sum_tree_with_flags(
1154 None,
1155 storage_flags.to_some_element_flags(),
1156 ),
1157 None => Element::empty_provable_count_sum_tree(),
1158 };
1159
1160 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1161 }
1162
1163 pub fn for_known_path_key_empty_provable_count_provable_sum_tree(
1179 path: Vec<Vec<u8>>,
1180 key: Vec<u8>,
1181 storage_flags: Option<&StorageFlags>,
1182 ) -> Self {
1183 let tree = match storage_flags {
1184 Some(storage_flags) => Element::new_provable_count_provable_sum_tree_with_flags(
1185 None,
1186 storage_flags.to_some_element_flags(),
1187 ),
1188 None => Element::empty_provable_count_provable_sum_tree(),
1189 };
1190
1191 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1192 }
1193
1194 pub fn for_estimated_path_key_empty_tree(
1196 path: KeyInfoPath,
1197 key: KeyInfo,
1198 storage_flags: Option<&StorageFlags>,
1199 ) -> Self {
1200 let tree = match storage_flags {
1201 Some(storage_flags) => {
1202 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
1203 }
1204 None => Element::empty_tree(),
1205 };
1206
1207 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1208 }
1209
1210 pub fn for_estimated_path_key_empty_sum_tree(
1212 path: KeyInfoPath,
1213 key: KeyInfo,
1214 storage_flags: Option<&StorageFlags>,
1215 ) -> Self {
1216 let tree = match storage_flags {
1217 Some(storage_flags) => {
1218 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
1219 }
1220 None => Element::empty_sum_tree(),
1221 };
1222
1223 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1224 }
1225
1226 pub fn for_estimated_path_key_empty_count_tree(
1228 path: KeyInfoPath,
1229 key: KeyInfo,
1230 storage_flags: Option<&StorageFlags>,
1231 ) -> Self {
1232 let tree = match storage_flags {
1233 Some(storage_flags) => {
1234 Element::empty_count_tree_with_flags(storage_flags.to_some_element_flags())
1235 }
1236 None => Element::empty_count_tree(),
1237 };
1238
1239 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1240 }
1241
1242 pub fn for_estimated_path_key_empty_provable_count_tree(
1244 path: KeyInfoPath,
1245 key: KeyInfo,
1246 storage_flags: Option<&StorageFlags>,
1247 ) -> Self {
1248 let tree = match storage_flags {
1249 Some(storage_flags) => {
1250 Element::empty_provable_count_tree_with_flags(storage_flags.to_some_element_flags())
1251 }
1252 None => Element::empty_provable_count_tree(),
1253 };
1254
1255 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1256 }
1257
1258 pub fn for_estimated_path_key_empty_provable_sum_tree(
1261 path: KeyInfoPath,
1262 key: KeyInfo,
1263 storage_flags: Option<&StorageFlags>,
1264 ) -> Self {
1265 let tree = match storage_flags {
1266 Some(storage_flags) => {
1267 Element::empty_provable_sum_tree_with_flags(storage_flags.to_some_element_flags())
1268 }
1269 None => Element::empty_provable_sum_tree(),
1270 };
1271
1272 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1273 }
1274
1275 pub fn for_estimated_path_key_empty_count_sum_tree(
1278 path: KeyInfoPath,
1279 key: KeyInfo,
1280 storage_flags: Option<&StorageFlags>,
1281 ) -> Self {
1282 let tree = match storage_flags {
1283 Some(storage_flags) => {
1284 Element::empty_count_sum_tree_with_flags(storage_flags.to_some_element_flags())
1285 }
1286 None => Element::empty_count_sum_tree(),
1287 };
1288
1289 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1290 }
1291
1292 pub fn for_estimated_path_key_empty_provable_count_sum_tree(
1296 path: KeyInfoPath,
1297 key: KeyInfo,
1298 storage_flags: Option<&StorageFlags>,
1299 ) -> Self {
1300 let tree = match storage_flags {
1301 Some(storage_flags) => Element::empty_provable_count_sum_tree_with_flags(
1302 storage_flags.to_some_element_flags(),
1303 ),
1304 None => Element::empty_provable_count_sum_tree(),
1305 };
1306
1307 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1308 }
1309
1310 pub fn for_estimated_path_key_empty_provable_count_provable_sum_tree(
1314 path: KeyInfoPath,
1315 key: KeyInfo,
1316 storage_flags: Option<&StorageFlags>,
1317 ) -> Self {
1318 let tree = match storage_flags {
1319 Some(storage_flags) => Element::empty_provable_count_provable_sum_tree_with_flags(
1320 storage_flags.to_some_element_flags(),
1321 ),
1322 None => Element::empty_provable_count_provable_sum_tree(),
1323 };
1324
1325 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1326 }
1327
1328 pub fn for_known_path_key_empty_provable_count_indexed_tree(
1345 path: Vec<Vec<u8>>,
1346 key: Vec<u8>,
1347 storage_flags: Option<&StorageFlags>,
1348 ) -> Self {
1349 let tree = match storage_flags {
1350 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1351 storage_flags.to_some_element_flags(),
1352 ),
1353 None => Element::empty_provable_count_indexed_tree(),
1354 };
1355
1356 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1357 }
1358
1359 pub fn for_known_path_key_empty_provable_sum_indexed_tree(
1365 path: Vec<Vec<u8>>,
1366 key: Vec<u8>,
1367 storage_flags: Option<&StorageFlags>,
1368 ) -> Self {
1369 let tree = match storage_flags {
1370 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1371 storage_flags.to_some_element_flags(),
1372 ),
1373 None => Element::empty_provable_sum_indexed_tree(),
1374 };
1375
1376 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1377 }
1378
1379 pub fn for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1392 path: Vec<Vec<u8>>,
1393 key: Vec<u8>,
1394 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1395 storage_flags: Option<&StorageFlags>,
1396 ) -> Result<Self, Error> {
1397 let tree = match storage_flags {
1398 Some(storage_flags) => {
1399 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1400 ranked_axes,
1401 storage_flags.to_some_element_flags(),
1402 )?
1403 }
1404 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1405 };
1406
1407 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1408 path, key, tree,
1409 ))
1410 }
1411
1412 pub fn for_known_path_key_empty_indexed_tree(
1422 path: Vec<Vec<u8>>,
1423 key: Vec<u8>,
1424 tree_type: TreeType,
1425 ranked_axes: &[IndexAxis],
1426 storage_flags: Option<&StorageFlags>,
1427 ) -> Result<Self, Error> {
1428 match (tree_type, ranked_axes) {
1429 (TreeType::ProvableCountIndexedTree, [IndexAxis::Count]) => {
1430 Ok(Self::for_known_path_key_empty_provable_count_indexed_tree(
1431 path,
1432 key,
1433 storage_flags,
1434 ))
1435 }
1436 (TreeType::ProvableSumIndexedTree, [IndexAxis::Sum]) => Ok(
1437 Self::for_known_path_key_empty_provable_sum_indexed_tree(path, key, storage_flags),
1438 ),
1439 (TreeType::ProvableCountProvableSumIndexedTree, axes) if !axes.is_empty() => {
1440 Self::for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1441 path,
1442 key,
1443 axes.iter().map(|axis| (axis.tag(), None)).collect(),
1444 storage_flags,
1445 )
1446 }
1447 _ => Err(Error::Drive(DriveError::NotSupported(
1448 "for_known_path_key_empty_indexed_tree called with a tree type / ranked-axis \
1449 pair that does not describe an indexed tree — the single-axis PCIT / PSIT \
1450 variants accept exactly their own axis and PCPSIT needs a non-empty axis list.",
1451 ))),
1452 }
1453 }
1454
1455 pub fn for_estimated_path_key_empty_provable_count_indexed_tree(
1458 path: KeyInfoPath,
1459 key: KeyInfo,
1460 storage_flags: Option<&StorageFlags>,
1461 ) -> Self {
1462 let tree = match storage_flags {
1463 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1464 storage_flags.to_some_element_flags(),
1465 ),
1466 None => Element::empty_provable_count_indexed_tree(),
1467 };
1468
1469 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1470 }
1471
1472 pub fn for_estimated_path_key_empty_provable_sum_indexed_tree(
1475 path: KeyInfoPath,
1476 key: KeyInfo,
1477 storage_flags: Option<&StorageFlags>,
1478 ) -> Self {
1479 let tree = match storage_flags {
1480 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1481 storage_flags.to_some_element_flags(),
1482 ),
1483 None => Element::empty_provable_sum_indexed_tree(),
1484 };
1485
1486 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1487 }
1488
1489 pub fn for_estimated_path_key_empty_provable_count_provable_sum_indexed_tree(
1492 path: KeyInfoPath,
1493 key: KeyInfo,
1494 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1495 storage_flags: Option<&StorageFlags>,
1496 ) -> Result<Self, Error> {
1497 let tree = match storage_flags {
1498 Some(storage_flags) => {
1499 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1500 ranked_axes,
1501 storage_flags.to_some_element_flags(),
1502 )?
1503 }
1504 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1505 };
1506
1507 Ok(LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree))
1508 }
1509
1510 pub fn insert_for_known_path_key_element(
1512 path: Vec<Vec<u8>>,
1513 key: Vec<u8>,
1514 element: Element,
1515 ) -> Self {
1516 GroveOperation(
1517 QualifiedGroveDbOp::insert_or_replace_op(path, key, element)
1518 .dont_check_for_backwards_references(),
1519 )
1520 }
1521
1522 pub fn replace_for_known_path_key_element(
1524 path: Vec<Vec<u8>>,
1525 key: Vec<u8>,
1526 element: Element,
1527 ) -> Self {
1528 GroveOperation(
1529 QualifiedGroveDbOp::replace_op(path, key, element)
1530 .dont_check_for_backwards_references(),
1531 )
1532 }
1533
1534 pub fn patch_for_known_path_key_element(
1537 path: Vec<Vec<u8>>,
1538 key: Vec<u8>,
1539 element: Element,
1540 change_in_bytes: i32,
1541 ) -> Self {
1542 GroveOperation(
1543 QualifiedGroveDbOp::patch_op(path, key, element, change_in_bytes)
1544 .dont_check_for_backwards_references(),
1545 )
1546 }
1547
1548 pub fn insert_for_estimated_path_key_element(
1550 path: KeyInfoPath,
1551 key: KeyInfo,
1552 element: Element,
1553 ) -> Self {
1554 GroveOperation(
1555 QualifiedGroveDbOp::insert_estimated_op(path, key, element)
1556 .dont_check_for_backwards_references(),
1557 )
1558 }
1559
1560 pub fn replace_for_estimated_path_key_element(
1562 path: KeyInfoPath,
1563 key: KeyInfo,
1564 element: Element,
1565 ) -> Self {
1566 GroveOperation(
1567 QualifiedGroveDbOp::replace_estimated_op(path, key, element)
1568 .dont_check_for_backwards_references(),
1569 )
1570 }
1571
1572 pub fn refresh_reference_for_known_path_key_reference_info(
1574 path: Vec<Vec<u8>>,
1575 key: Vec<u8>,
1576 reference_path_type: ReferencePathType,
1577 max_reference_hop: MaxReferenceHop,
1578 flags: Option<ElementFlags>,
1579 trust_refresh_reference: bool,
1580 ) -> Self {
1581 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
1582 path,
1583 key,
1584 reference_path_type,
1585 max_reference_hop,
1586 flags,
1587 false,
1594 trust_refresh_reference,
1595 ))
1596 }
1597
1598 pub fn refresh_reference_with_sum_item_for_known_path_key_reference_info(
1612 path: Vec<Vec<u8>>,
1613 key: Vec<u8>,
1614 reference_path_type: ReferencePathType,
1615 max_reference_hop: MaxReferenceHop,
1616 sum_value: i64,
1617 flags: Option<ElementFlags>,
1618 trust_refresh_reference: bool,
1619 ) -> Self {
1620 GroveOperation(QualifiedGroveDbOp::refresh_reference_with_sum_item_op(
1621 path,
1622 key,
1623 reference_path_type,
1624 max_reference_hop,
1625 sum_value,
1626 flags,
1627 false,
1632 trust_refresh_reference,
1633 ))
1634 }
1635}
1636
1637pub trait LowLevelDriveOperationTreeTypeConverter {
1639 fn empty_tree_operation_for_known_path_key(
1641 &self,
1642 path: Vec<Vec<u8>>,
1643 key: Vec<u8>,
1644 storage_flags: Option<&StorageFlags>,
1645 ) -> Result<LowLevelDriveOperation, Error>;
1646}
1647
1648impl LowLevelDriveOperationTreeTypeConverter for TreeType {
1649 fn empty_tree_operation_for_known_path_key(
1651 &self,
1652 path: Vec<Vec<u8>>,
1653 key: Vec<u8>,
1654 storage_flags: Option<&StorageFlags>,
1655 ) -> Result<LowLevelDriveOperation, Error> {
1656 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
1657 let element = match self {
1658 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1659 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1660 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1661 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1662 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1663 TreeType::ProvableCountTree => {
1664 Element::empty_provable_count_tree_with_flags(element_flags)
1665 }
1666 TreeType::ProvableCountSumTree => {
1667 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1668 }
1669 TreeType::ProvableCountProvableSumTree => {
1670 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1671 }
1672 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1673 TreeType::CommitmentTree(chunk_power) => {
1674 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
1675 }
1676 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
1677 TreeType::BulkAppendTree(chunk_power) => {
1678 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
1679 }
1680 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
1681 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
1682 }
1683 TreeType::ProvableSumIndexedTree => {
1688 Element::empty_provable_sum_indexed_tree_with_flags(element_flags)
1689 }
1690 TreeType::ProvableCountIndexedTree => {
1691 Element::empty_provable_count_indexed_tree_with_flags(element_flags)
1692 }
1693 TreeType::ProvableCountProvableSumIndexedTree => {
1705 return Err(Error::Drive(DriveError::NotSupported(
1706 "empty_tree_operation_for_known_path_key cannot create a \
1707 ProvableCountProvableSumIndexedTree — the ranked axis set is not carried \
1708 by TreeType; use for_known_path_key_empty_indexed_tree (or \
1709 batch_insert_empty_provable_count_provable_sum_indexed_tree) instead.",
1710 )))
1711 }
1712 TreeType::PrivateDocumentStore(_) => {
1718 return Err(Error::Drive(DriveError::NotSupported(
1719 "empty_tree_operation_for_known_path_key cannot create a \
1720 PrivateDocumentStore — the entry size is not carried by TreeType",
1721 )))
1722 }
1723 };
1724
1725 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1726 path, key, element,
1727 ))
1728 }
1729}
1730
1731pub trait DriveCost {
1733 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
1735}
1736
1737impl DriveCost for OperationCost {
1738 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
1740 let OperationCost {
1741 seek_count,
1742 storage_cost,
1743 storage_loaded_bytes,
1744 hash_node_calls,
1745 sinsemilla_hash_calls,
1746 } = self;
1747 let epoch_cost_for_processing_credit_per_byte =
1748 fee_version.storage.storage_processing_credit_per_byte;
1749 let seek_cost = (*seek_count as u64)
1750 .checked_mul(fee_version.storage.storage_seek_cost)
1751 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
1752 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
1753 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1754 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1755 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
1756 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1757 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1758 let storage_removed_bytes_ephemeral_cost =
1759 (storage_cost.removed_bytes.total_removed_bytes() as u64)
1760 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1761 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1762 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
1764 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
1765 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
1766
1767 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
1769 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
1771 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
1772 seek_cost
1773 .checked_add(storage_added_bytes_ephemeral_cost)
1774 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
1775 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
1776 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
1777 .and_then(|c| c.checked_add(hash_node_cost))
1778 .and_then(|c| c.checked_add(sinsemilla_cost))
1779 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
1780 }
1781}
1782
1783#[cfg(test)]
1784#[allow(clippy::identity_op)]
1785mod tests {
1786 use super::*;
1787 use grovedb_costs::storage_cost::removal::StorageRemovedBytes;
1788 use grovedb_costs::storage_cost::StorageCost;
1789 use platform_version::version::fee::storage::FeeStorageVersion;
1790 use platform_version::version::fee::FeeVersion;
1791
1792 fn fee_version() -> &'static FeeVersion {
1794 FeeVersion::first()
1795 }
1796
1797 #[test]
1802 fn base_op_stop_costs_zero() {
1803 assert_eq!(BaseOp::Stop.cost(), 0);
1804 }
1805
1806 #[test]
1807 fn base_op_add_costs_12() {
1808 assert_eq!(BaseOp::Add.cost(), 12);
1809 }
1810
1811 #[test]
1812 fn base_op_mul_costs_20() {
1813 assert_eq!(BaseOp::Mul.cost(), 20);
1814 }
1815
1816 #[test]
1817 fn base_op_signextend_costs_20() {
1818 assert_eq!(BaseOp::Signextend.cost(), 20);
1819 }
1820
1821 #[test]
1822 fn base_op_addmod_costs_32() {
1823 assert_eq!(BaseOp::Addmod.cost(), 32);
1824 }
1825
1826 #[test]
1827 fn base_op_mulmod_costs_32() {
1828 assert_eq!(BaseOp::Mulmod.cost(), 32);
1829 }
1830
1831 #[test]
1832 fn base_op_byte_costs_12() {
1833 assert_eq!(BaseOp::Byte.cost(), 12);
1834 }
1835
1836 #[test]
1837 fn base_op_sub_costs_12() {
1838 assert_eq!(BaseOp::Sub.cost(), 12);
1839 }
1840
1841 #[test]
1842 fn base_op_div_costs_20() {
1843 assert_eq!(BaseOp::Div.cost(), 20);
1844 }
1845
1846 #[test]
1847 fn base_op_comparison_ops_all_cost_12() {
1848 for op in [
1849 BaseOp::Lt,
1850 BaseOp::Gt,
1851 BaseOp::Slt,
1852 BaseOp::Sgt,
1853 BaseOp::Eq,
1854 BaseOp::Iszero,
1855 ] {
1856 assert_eq!(op.cost(), 12, "comparison op {:?} should cost 12", op);
1857 }
1858 }
1859
1860 #[test]
1861 fn base_op_bitwise_ops_all_cost_12() {
1862 for op in [BaseOp::And, BaseOp::Or, BaseOp::Xor, BaseOp::Not] {
1863 assert_eq!(op.cost(), 12, "bitwise op {:?} should cost 12", op);
1864 }
1865 }
1866
1867 #[test]
1872 fn hash_function_block_size_all_64() {
1873 assert_eq!(HashFunction::Sha256.block_size(), 64);
1875 assert_eq!(HashFunction::Sha256_2.block_size(), 64);
1876 assert_eq!(HashFunction::Blake3.block_size(), 64);
1877 assert_eq!(HashFunction::Sha256RipeMD160.block_size(), 64);
1878 }
1879
1880 #[test]
1881 fn hash_function_rounds() {
1882 assert_eq!(HashFunction::Sha256.rounds(), 1);
1883 assert_eq!(HashFunction::Sha256_2.rounds(), 2);
1884 assert_eq!(HashFunction::Blake3.rounds(), 1);
1885 assert_eq!(HashFunction::Sha256RipeMD160.rounds(), 1);
1886 }
1887
1888 #[test]
1889 fn hash_function_block_cost_sha256_variants_use_sha256_per_block() {
1890 let fv = fee_version();
1891 let expected = fv.hashing.sha256_per_block;
1892 assert_eq!(HashFunction::Sha256.block_cost(fv), expected);
1893 assert_eq!(HashFunction::Sha256_2.block_cost(fv), expected);
1894 assert_eq!(HashFunction::Sha256RipeMD160.block_cost(fv), expected);
1895 }
1896
1897 #[test]
1898 fn hash_function_block_cost_blake3_uses_blake3_per_block() {
1899 let fv = fee_version();
1900 assert_eq!(
1901 HashFunction::Blake3.block_cost(fv),
1902 fv.hashing.blake3_per_block
1903 );
1904 }
1905
1906 #[test]
1907 fn hash_function_base_cost_sha256() {
1908 let fv = fee_version();
1909 assert_eq!(
1910 HashFunction::Sha256.base_cost(fv),
1911 fv.hashing.single_sha256_base
1912 );
1913 }
1914
1915 #[test]
1916 fn hash_function_base_cost_sha256_2_uses_single_sha256_base() {
1917 let fv = fee_version();
1918 assert_eq!(
1920 HashFunction::Sha256_2.base_cost(fv),
1921 fv.hashing.single_sha256_base
1922 );
1923 }
1924
1925 #[test]
1926 fn hash_function_base_cost_blake3() {
1927 let fv = fee_version();
1928 assert_eq!(HashFunction::Blake3.base_cost(fv), fv.hashing.blake3_base);
1929 }
1930
1931 #[test]
1932 fn hash_function_base_cost_sha256_ripe_md160() {
1933 let fv = fee_version();
1934 assert_eq!(
1935 HashFunction::Sha256RipeMD160.base_cost(fv),
1936 fv.hashing.sha256_ripe_md160_base
1937 );
1938 }
1939
1940 #[test]
1945 fn function_op_new_with_byte_count_small_sha256() {
1946 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 32);
1948 assert_eq!(op.rounds, 1);
1949 assert_eq!(op.hash, HashFunction::Sha256);
1950 }
1951
1952 #[test]
1953 fn function_op_new_with_byte_count_exact_block_boundary_sha256() {
1954 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 64);
1956 assert_eq!(op.rounds, 2);
1957 }
1958
1959 #[test]
1960 fn function_op_new_with_byte_count_large_sha256() {
1961 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 200);
1963 assert_eq!(op.rounds, 4);
1964 }
1965
1966 #[test]
1967 fn function_op_new_with_byte_count_sha256_2_has_extra_round() {
1968 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 32);
1970 assert_eq!(op.rounds, 2);
1971 }
1972
1973 #[test]
1974 fn function_op_new_with_byte_count_sha256_2_large() {
1975 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 200);
1977 assert_eq!(op.rounds, 5);
1978 }
1979
1980 #[test]
1981 fn function_op_new_with_byte_count_blake3_small() {
1982 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 10);
1984 assert_eq!(op.rounds, 1);
1985 assert_eq!(op.hash, HashFunction::Blake3);
1986 }
1987
1988 #[test]
1989 fn function_op_new_with_byte_count_blake3_large() {
1990 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 500);
1992 assert_eq!(op.rounds, 8);
1993 }
1994
1995 #[test]
1996 fn function_op_new_with_byte_count_zero_bytes() {
1997 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 0);
1999 assert_eq!(op.rounds, 1);
2000 }
2001
2002 #[test]
2003 fn function_op_new_with_byte_count_sha256_ripemd160() {
2004 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256RipeMD160, 20);
2006 assert_eq!(op.rounds, 1);
2007 assert_eq!(op.hash, HashFunction::Sha256RipeMD160);
2008 }
2009
2010 #[test]
2015 fn function_op_cost_sha256_one_round() {
2016 let fv = fee_version();
2017 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 1);
2018 let expected = fv.hashing.single_sha256_base + 1 * fv.hashing.sha256_per_block;
2020 assert_eq!(op.cost(fv), expected);
2021 }
2022
2023 #[test]
2024 fn function_op_cost_sha256_2_two_rounds() {
2025 let fv = fee_version();
2026 let op = FunctionOp::new_with_round_count(HashFunction::Sha256_2, 2);
2027 let expected = fv.hashing.single_sha256_base + 2 * fv.hashing.sha256_per_block;
2029 assert_eq!(op.cost(fv), expected);
2030 }
2031
2032 #[test]
2033 fn function_op_cost_blake3_one_round() {
2034 let fv = fee_version();
2035 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 1);
2036 let expected = fv.hashing.blake3_base + 1 * fv.hashing.blake3_per_block;
2038 assert_eq!(op.cost(fv), expected);
2039 }
2040
2041 #[test]
2042 fn function_op_cost_zero_rounds() {
2043 let fv = fee_version();
2044 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 0);
2045 assert_eq!(op.cost(fv), fv.hashing.blake3_base);
2047 }
2048
2049 #[test]
2050 fn function_op_cost_from_byte_count_matches_manual_calc() {
2051 let fv = fee_version();
2052 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 128);
2054 assert_eq!(op.rounds, 3);
2055 let expected = fv.hashing.single_sha256_base + 3 * fv.hashing.sha256_per_block;
2056 assert_eq!(op.cost(fv), expected);
2057 }
2058
2059 #[test]
2060 fn function_op_cost_sha256_ripemd160() {
2061 let fv = fee_version();
2062 let op = FunctionOp::new_with_round_count(HashFunction::Sha256RipeMD160, 1);
2063 let expected = fv.hashing.sha256_ripe_md160_base + 1 * fv.hashing.sha256_per_block;
2064 assert_eq!(op.cost(fv), expected);
2065 }
2066
2067 #[test]
2068 fn function_op_cost_saturating_mul_does_not_panic_on_large_rounds() {
2069 let fv = fee_version();
2070 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, u32::MAX);
2071 let expected_block_cost = (u32::MAX as u64).saturating_mul(fv.hashing.sha256_per_block);
2074 let expected = fv
2075 .hashing
2076 .single_sha256_base
2077 .saturating_add(expected_block_cost);
2078 assert_eq!(op.cost(fv), expected);
2079 }
2080
2081 #[test]
2082 fn function_op_cost_saturates_to_max_with_extreme_fee_version() {
2083 let mut fv = fee_version().clone();
2086 fv.hashing.sha256_per_block = u64::MAX;
2087 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 2);
2088 assert_eq!(op.cost(&fv), u64::MAX);
2090 }
2091
2092 #[test]
2097 fn operation_cost_calculated_cost_operation_returns_cost() {
2098 let cost = OperationCost {
2099 seek_count: 3,
2100 storage_cost: StorageCost {
2101 added_bytes: 100,
2102 replaced_bytes: 50,
2103 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2104 },
2105 storage_loaded_bytes: 200,
2106 hash_node_calls: 5,
2107 sinsemilla_hash_calls: 0,
2108 };
2109 let op = CalculatedCostOperation(cost.clone());
2110 let result = op.operation_cost().expect("should return Ok");
2111 assert_eq!(result, cost);
2112 }
2113
2114 #[test]
2115 fn operation_cost_grove_operation_returns_error() {
2116 let grove_op = LowLevelDriveOperation::insert_for_known_path_key_element(
2117 vec![vec![1, 2, 3]],
2118 vec![4, 5, 6],
2119 Element::empty_tree(),
2120 );
2121 let result = grove_op.operation_cost();
2122 assert!(result.is_err());
2123 let err_msg = format!("{:?}", result.unwrap_err());
2124 assert!(
2125 err_msg.contains("grove operations must be executed"),
2126 "unexpected error: {}",
2127 err_msg
2128 );
2129 }
2130
2131 #[test]
2132 fn operation_cost_pre_calculated_fee_result_returns_error() {
2133 let fee = FeeResult {
2134 storage_fee: 100,
2135 processing_fee: 200,
2136 ..Default::default()
2137 };
2138 let op = PreCalculatedFeeResult(fee);
2139 let result = op.operation_cost();
2140 assert!(result.is_err());
2141 let err_msg = format!("{:?}", result.unwrap_err());
2142 assert!(
2143 err_msg.contains("pre calculated fees should not be requested"),
2144 "unexpected error: {}",
2145 err_msg
2146 );
2147 }
2148
2149 #[test]
2150 fn operation_cost_function_operation_returns_error() {
2151 let func_op = FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1));
2152 let result = func_op.operation_cost();
2153 assert!(result.is_err());
2154 let err_msg = format!("{:?}", result.unwrap_err());
2155 assert!(
2156 err_msg.contains("function operations should not be requested"),
2157 "unexpected error: {}",
2158 err_msg
2159 );
2160 }
2161
2162 #[test]
2167 fn combine_cost_operations_sums_calculated_costs_only() {
2168 let cost1 = OperationCost {
2169 seek_count: 2,
2170 storage_cost: StorageCost {
2171 added_bytes: 10,
2172 replaced_bytes: 0,
2173 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2174 },
2175 storage_loaded_bytes: 50,
2176 hash_node_calls: 1,
2177 sinsemilla_hash_calls: 0,
2178 };
2179 let cost2 = OperationCost {
2180 seek_count: 3,
2181 storage_cost: StorageCost {
2182 added_bytes: 20,
2183 replaced_bytes: 5,
2184 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2185 },
2186 storage_loaded_bytes: 100,
2187 hash_node_calls: 2,
2188 sinsemilla_hash_calls: 1,
2189 };
2190
2191 let operations = vec![
2192 CalculatedCostOperation(cost1.clone()),
2193 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2195 CalculatedCostOperation(cost2.clone()),
2196 PreCalculatedFeeResult(FeeResult::default()),
2198 ];
2199
2200 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2201 assert_eq!(combined.seek_count, 2 + 3);
2202 assert_eq!(combined.storage_cost.added_bytes, 10 + 20);
2203 assert_eq!(combined.storage_cost.replaced_bytes, 0 + 5);
2204 assert_eq!(combined.storage_loaded_bytes, 50 + 100);
2205 assert_eq!(combined.hash_node_calls, 1 + 2);
2206 assert_eq!(combined.sinsemilla_hash_calls, 0 + 1);
2207 }
2208
2209 #[test]
2210 fn combine_cost_operations_empty_list_returns_default() {
2211 let combined = LowLevelDriveOperation::combine_cost_operations(&[]);
2212 assert_eq!(combined, OperationCost::default());
2213 }
2214
2215 #[test]
2216 fn combine_cost_operations_no_calculated_costs_returns_default() {
2217 let operations = vec![
2218 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 2)),
2219 PreCalculatedFeeResult(FeeResult {
2220 processing_fee: 999,
2221 ..Default::default()
2222 }),
2223 ];
2224 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2225 assert_eq!(combined, OperationCost::default());
2226 }
2227
2228 fn make_grove_op(key_byte: u8) -> LowLevelDriveOperation {
2234 LowLevelDriveOperation::insert_for_known_path_key_element(
2235 vec![vec![0]],
2236 vec![key_byte],
2237 Element::new_item(vec![key_byte]),
2238 )
2239 }
2240
2241 fn make_mixed_ops() -> Vec<LowLevelDriveOperation> {
2242 vec![
2243 make_grove_op(1),
2244 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2245 make_grove_op(2),
2246 CalculatedCostOperation(OperationCost::default()),
2247 make_grove_op(3),
2248 ]
2249 }
2250
2251 #[test]
2252 fn grovedb_operations_batch_filters_grove_ops_from_ref() {
2253 let ops = make_mixed_ops();
2254 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2255 assert_eq!(batch.len(), 3);
2256 }
2257
2258 #[test]
2259 fn grovedb_operations_batch_empty_input() {
2260 let batch = LowLevelDriveOperation::grovedb_operations_batch(&[]);
2261 assert!(batch.is_empty());
2262 }
2263
2264 #[test]
2265 fn grovedb_operations_batch_no_grove_ops() {
2266 let ops = vec![
2267 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1)),
2268 CalculatedCostOperation(OperationCost::default()),
2269 ];
2270 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2271 assert!(batch.is_empty());
2272 }
2273
2274 #[test]
2275 fn grovedb_operations_batch_consume_filters_grove_ops() {
2276 let ops = make_mixed_ops();
2277 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(ops);
2278 assert_eq!(batch.len(), 3);
2279 }
2280
2281 #[test]
2282 fn grovedb_operations_batch_consume_empty_input() {
2283 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(vec![]);
2284 assert!(batch.is_empty());
2285 }
2286
2287 #[test]
2288 fn grovedb_operations_batch_consume_with_leftovers_partitions_correctly() {
2289 let ops = make_mixed_ops();
2290 let (batch, leftovers) =
2291 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2292 assert_eq!(batch.len(), 3);
2293 assert_eq!(leftovers.len(), 2);
2294
2295 for leftover in &leftovers {
2297 assert!(
2298 !matches!(leftover, GroveOperation(_)),
2299 "leftovers should not contain GroveOperation variants"
2300 );
2301 }
2302 }
2303
2304 #[test]
2305 fn grovedb_operations_batch_consume_with_leftovers_all_grove() {
2306 let ops = vec![make_grove_op(10), make_grove_op(20)];
2307 let (batch, leftovers) =
2308 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2309 assert_eq!(batch.len(), 2);
2310 assert!(leftovers.is_empty());
2311 }
2312
2313 #[test]
2314 fn grovedb_operations_batch_consume_with_leftovers_no_grove() {
2315 let ops = vec![
2316 CalculatedCostOperation(OperationCost::default()),
2317 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2318 ];
2319 let (batch, leftovers) =
2320 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2321 assert!(batch.is_empty());
2322 assert_eq!(leftovers.len(), 2);
2323 }
2324
2325 #[test]
2326 fn grovedb_operations_batch_consume_with_leftovers_keeps_ephemeral_ops() {
2327 let ops = vec![
2328 make_grove_op(10),
2329 make_grove_op(20).retag_ephemeral(),
2330 CalculatedCostOperation(OperationCost::default()),
2331 ];
2332 let (batch, leftovers) =
2333 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2334 assert_eq!(batch.len(), 1, "only the ordinary grove op joins the batch");
2335 assert_eq!(leftovers.len(), 2);
2336 assert!(
2337 leftovers
2338 .iter()
2339 .any(|op| matches!(op, EphemeralGroveOperation(_))),
2340 "an ephemeral grove op must survive as a leftover, never be dropped"
2341 );
2342 }
2343
2344 #[test]
2345 fn grovedb_operations_batch_consume_with_leftovers_empty() {
2346 let (batch, leftovers) =
2347 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(vec![]);
2348 assert!(batch.is_empty());
2349 assert!(leftovers.is_empty());
2350 }
2351
2352 #[test]
2357 fn ephemeral_cost_zero_operation() {
2358 let fv = fee_version();
2359 let cost = OperationCost::default();
2360 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2361 assert_eq!(result, 0);
2362 }
2363
2364 #[test]
2365 fn ephemeral_cost_seek_only() {
2366 let fv = fee_version();
2367 let cost = OperationCost {
2368 seek_count: 5,
2369 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).expect("should not overflow");
2375 let expected = 5u64 * fv.storage.storage_seek_cost;
2376 assert_eq!(result, expected);
2377 }
2378
2379 #[test]
2380 fn ephemeral_cost_storage_added_bytes() {
2381 let fv = fee_version();
2382 let cost = OperationCost {
2383 seek_count: 0,
2384 storage_cost: StorageCost {
2385 added_bytes: 100,
2386 replaced_bytes: 0,
2387 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2388 },
2389 storage_loaded_bytes: 0,
2390 hash_node_calls: 0,
2391 sinsemilla_hash_calls: 0,
2392 };
2393 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2394 let expected = 100u64 * fv.storage.storage_processing_credit_per_byte;
2395 assert_eq!(result, expected);
2396 }
2397
2398 #[test]
2399 fn ephemeral_cost_storage_replaced_bytes() {
2400 let fv = fee_version();
2401 let cost = OperationCost {
2402 seek_count: 0,
2403 storage_cost: StorageCost {
2404 added_bytes: 0,
2405 replaced_bytes: 50,
2406 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2407 },
2408 storage_loaded_bytes: 0,
2409 hash_node_calls: 0,
2410 sinsemilla_hash_calls: 0,
2411 };
2412 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2413 let expected = 50u64 * fv.storage.storage_processing_credit_per_byte;
2414 assert_eq!(result, expected);
2415 }
2416
2417 #[test]
2418 fn ephemeral_cost_storage_removed_bytes_basic() {
2419 let fv = fee_version();
2420 let cost = OperationCost {
2421 seek_count: 0,
2422 storage_cost: StorageCost {
2423 added_bytes: 0,
2424 replaced_bytes: 0,
2425 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(75),
2426 },
2427 storage_loaded_bytes: 0,
2428 hash_node_calls: 0,
2429 sinsemilla_hash_calls: 0,
2430 };
2431 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2432 let expected = 75u64 * fv.storage.storage_processing_credit_per_byte;
2433 assert_eq!(result, expected);
2434 }
2435
2436 #[test]
2437 fn ephemeral_cost_loaded_bytes() {
2438 let fv = fee_version();
2439 let cost = OperationCost {
2440 seek_count: 0,
2441 storage_cost: StorageCost::default(),
2442 storage_loaded_bytes: 300,
2443 hash_node_calls: 0,
2444 sinsemilla_hash_calls: 0,
2445 };
2446 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2447 let expected = 300u64 * fv.storage.storage_load_credit_per_byte;
2448 assert_eq!(result, expected);
2449 }
2450
2451 #[test]
2452 fn ephemeral_cost_hash_node_calls() {
2453 let fv = fee_version();
2454 let cost = OperationCost {
2455 seek_count: 0,
2456 storage_cost: StorageCost::default(),
2457 storage_loaded_bytes: 0,
2458 hash_node_calls: 10,
2459 sinsemilla_hash_calls: 0,
2460 };
2461 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2462 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2463 let expected = blake3_total * 10;
2464 assert_eq!(result, expected);
2465 }
2466
2467 #[test]
2468 fn ephemeral_cost_sinsemilla_hash_calls() {
2469 let fv = fee_version();
2470 let cost = OperationCost {
2471 seek_count: 0,
2472 storage_cost: StorageCost::default(),
2473 storage_loaded_bytes: 0,
2474 hash_node_calls: 0,
2475 sinsemilla_hash_calls: 3,
2476 };
2477 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2478 let expected = fv.hashing.sinsemilla_base * 3;
2479 assert_eq!(result, expected);
2480 }
2481
2482 #[test]
2483 fn ephemeral_cost_all_components_combined() {
2484 let fv = fee_version();
2485 let cost = OperationCost {
2486 seek_count: 2,
2487 storage_cost: StorageCost {
2488 added_bytes: 10,
2489 replaced_bytes: 20,
2490 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(30),
2491 },
2492 storage_loaded_bytes: 40,
2493 hash_node_calls: 5,
2494 sinsemilla_hash_calls: 1,
2495 };
2496 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2497
2498 let seek_cost = 2u64 * fv.storage.storage_seek_cost;
2499 let processing_per_byte = fv.storage.storage_processing_credit_per_byte;
2500 let added_cost = 10u64 * processing_per_byte;
2501 let replaced_cost = 20u64 * processing_per_byte;
2502 let removed_cost = 30u64 * processing_per_byte;
2503 let loaded_cost = 40u64 * fv.storage.storage_load_credit_per_byte;
2504 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2505 let hash_cost = blake3_total * 5;
2506 let sinsemilla_cost = fv.hashing.sinsemilla_base * 1;
2507
2508 let expected = seek_cost
2509 + added_cost
2510 + replaced_cost
2511 + loaded_cost
2512 + removed_cost
2513 + hash_cost
2514 + sinsemilla_cost;
2515 assert_eq!(result, expected);
2516 }
2517
2518 #[test]
2519 fn ephemeral_cost_overflow_seek_cost() {
2520 let fv = &FeeVersion {
2521 storage: FeeStorageVersion {
2522 storage_seek_cost: u64::MAX,
2523 ..fee_version().storage.clone()
2524 },
2525 ..fee_version().clone()
2526 };
2527 let cost = OperationCost {
2528 seek_count: 2, storage_cost: StorageCost::default(),
2530 storage_loaded_bytes: 0,
2531 hash_node_calls: 0,
2532 sinsemilla_hash_calls: 0,
2533 };
2534 let result = cost.ephemeral_cost(fv);
2535 assert!(result.is_err(), "expected overflow error for seek cost");
2536 }
2537
2538 #[test]
2539 fn ephemeral_cost_overflow_storage_written_bytes() {
2540 let fv = &FeeVersion {
2541 storage: FeeStorageVersion {
2542 storage_processing_credit_per_byte: u64::MAX,
2543 ..fee_version().storage.clone()
2544 },
2545 ..fee_version().clone()
2546 };
2547 let cost = OperationCost {
2548 seek_count: 0,
2549 storage_cost: StorageCost {
2550 added_bytes: 2, replaced_bytes: 0,
2552 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2553 },
2554 storage_loaded_bytes: 0,
2555 hash_node_calls: 0,
2556 sinsemilla_hash_calls: 0,
2557 };
2558 let result = cost.ephemeral_cost(fv);
2559 assert!(
2560 result.is_err(),
2561 "expected overflow error for storage written bytes"
2562 );
2563 }
2564
2565 #[test]
2566 fn ephemeral_cost_overflow_loaded_bytes() {
2567 let fv = &FeeVersion {
2568 storage: FeeStorageVersion {
2569 storage_load_credit_per_byte: u64::MAX,
2570 ..fee_version().storage.clone()
2571 },
2572 ..fee_version().clone()
2573 };
2574 let cost = OperationCost {
2575 seek_count: 0,
2576 storage_cost: StorageCost::default(),
2577 storage_loaded_bytes: 2, hash_node_calls: 0,
2579 sinsemilla_hash_calls: 0,
2580 };
2581 let result = cost.ephemeral_cost(fv);
2582 assert!(
2583 result.is_err(),
2584 "expected overflow error for loaded bytes cost"
2585 );
2586 }
2587
2588 #[test]
2594 fn empty_tree_operation_for_known_path_key_provable_sum_tree() {
2595 use grovedb::batch::GroveOp;
2596
2597 let op = TreeType::ProvableSumTree
2598 .empty_tree_operation_for_known_path_key(vec![b"root".to_vec()], b"k".to_vec(), None)
2599 .expect("empty_tree_operation_for_known_path_key");
2600
2601 match op {
2602 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2603 GroveOp::InsertOrReplace { element }
2604 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element } => assert!(
2605 matches!(element, Element::ProvableSumTree(..)),
2606 "expected ProvableSumTree element, got: {:?}",
2607 element
2608 ),
2609 other => panic!("expected GroveOp::InsertOrReplace, got: {:?}", other),
2610 },
2611 other => panic!("expected GroveOperation, got: {:?}", other),
2612 }
2613 }
2614
2615 #[test]
2623 fn zero_contribution_dispatcher_full_matrix() {
2624 use grovedb::batch::GroveOp;
2625
2626 const ALL_INNERS: [TreeType; 9] = [
2627 TreeType::NormalTree,
2628 TreeType::SumTree,
2629 TreeType::BigSumTree,
2630 TreeType::CountTree,
2631 TreeType::CountSumTree,
2632 TreeType::ProvableCountTree,
2633 TreeType::ProvableCountSumTree,
2634 TreeType::ProvableSumTree,
2635 TreeType::ProvableCountProvableSumTree,
2636 ];
2637
2638 fn is_sum_bearing(tree_type: TreeType) -> bool {
2639 matches!(
2640 tree_type,
2641 TreeType::SumTree
2642 | TreeType::BigSumTree
2643 | TreeType::CountSumTree
2644 | TreeType::ProvableCountSumTree
2645 | TreeType::ProvableSumTree
2646 | TreeType::ProvableCountProvableSumTree
2647 )
2648 }
2649
2650 fn element_tree_type(element: &Element) -> TreeType {
2651 match element {
2652 Element::Tree(..) => TreeType::NormalTree,
2653 Element::SumTree(..) => TreeType::SumTree,
2654 Element::BigSumTree(..) => TreeType::BigSumTree,
2655 Element::CountTree(..) => TreeType::CountTree,
2656 Element::CountSumTree(..) => TreeType::CountSumTree,
2657 Element::ProvableCountTree(..) => TreeType::ProvableCountTree,
2658 Element::ProvableCountSumTree(..) => TreeType::ProvableCountSumTree,
2659 Element::ProvableSumTree(..) => TreeType::ProvableSumTree,
2660 Element::ProvableCountProvableSumTree(..) => TreeType::ProvableCountProvableSumTree,
2661 other => panic!("unexpected inner element: {other:?}"),
2662 }
2663 }
2664
2665 #[derive(Debug, PartialEq)]
2666 enum Expected {
2667 NonCounted,
2668 NotSummed,
2669 NotCountedOrSummed,
2670 Unwrapped,
2671 }
2672
2673 let dispatch = |parent: TreeType, inner: TreeType| {
2674 LowLevelDriveOperation::for_known_path_key_empty_tree_contributing_zero_to_parent(
2675 vec![b"root".to_vec()],
2676 b"key".to_vec(),
2677 parent,
2678 inner,
2679 None,
2680 )
2681 };
2682
2683 let assert_cell = |parent: TreeType, inner: TreeType, expected: Expected| {
2684 let op = dispatch(parent, inner).unwrap_or_else(|error| {
2685 panic!("parent {parent:?} inner {inner:?} must be accepted: {error}")
2686 });
2687 let element = match op {
2688 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2689 GroveOp::InsertOrReplace { element }
2690 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element } => {
2691 element
2692 }
2693 other => panic!("expected InsertOrReplace, got {other:?}"),
2694 },
2695 other => panic!("expected GroveOperation, got {other:?}"),
2696 };
2697 let (wrapper, produced_inner) = match &element {
2698 Element::NonCounted(inner_element) => {
2699 (Expected::NonCounted, inner_element.as_ref())
2700 }
2701 Element::NotSummed(inner_element) => (Expected::NotSummed, inner_element.as_ref()),
2702 Element::NotCountedOrSummed(inner_element) => {
2703 (Expected::NotCountedOrSummed, inner_element.as_ref())
2704 }
2705 plain => (Expected::Unwrapped, plain),
2706 };
2707 assert_eq!(
2708 wrapper, expected,
2709 "parent {parent:?} inner {inner:?}: wrong wrapper"
2710 );
2711 assert_eq!(
2712 element_tree_type(produced_inner),
2713 inner,
2714 "parent {parent:?} inner {inner:?}: wrong inner tree type"
2715 );
2716 };
2717
2718 for inner in ALL_INNERS {
2720 assert_cell(TreeType::CountTree, inner, Expected::NonCounted);
2721 }
2722 for inner in ALL_INNERS {
2725 let expected = if is_sum_bearing(inner) {
2726 Expected::NotCountedOrSummed
2727 } else {
2728 Expected::NonCounted
2729 };
2730 assert_cell(TreeType::CountSumTree, inner, expected);
2731 }
2732 for parent in [
2735 TreeType::SumTree,
2736 TreeType::BigSumTree,
2737 TreeType::ProvableSumTree,
2738 ] {
2739 for inner in ALL_INNERS {
2740 let expected = if is_sum_bearing(inner) {
2741 Expected::NotSummed
2742 } else {
2743 Expected::Unwrapped
2744 };
2745 assert_cell(parent, inner, expected);
2746 }
2747 }
2748 for parent in [
2753 TreeType::NormalTree,
2754 TreeType::ProvableCountTree,
2755 TreeType::ProvableCountSumTree,
2756 TreeType::ProvableCountProvableSumTree,
2757 ] {
2758 for inner in ALL_INNERS {
2759 assert!(
2760 dispatch(parent, inner).is_err(),
2761 "parent {parent:?} inner {inner:?} must be rejected"
2762 );
2763 }
2764 }
2765
2766 const INDEXED: [TreeType; 3] = [
2772 TreeType::ProvableCountIndexedTree,
2773 TreeType::ProvableSumIndexedTree,
2774 TreeType::ProvableCountProvableSumIndexedTree,
2775 ];
2776 for indexed in INDEXED {
2777 for inner in ALL_INNERS {
2778 assert!(
2779 dispatch(indexed, inner).is_err(),
2780 "indexed parent {indexed:?} inner {inner:?} must be rejected"
2781 );
2782 }
2783 for parent in [
2784 TreeType::CountTree,
2785 TreeType::CountSumTree,
2786 TreeType::SumTree,
2787 TreeType::BigSumTree,
2788 TreeType::ProvableSumTree,
2789 ] {
2790 assert!(
2791 dispatch(parent, indexed).is_err(),
2792 "parent {parent:?} indexed inner {indexed:?} must be rejected"
2793 );
2794 }
2795 }
2796 }
2797
2798 #[test]
2799 fn ephemeral_cost_overflow_in_addition_chain() {
2800 let fv = fee_version();
2802 let cost = OperationCost {
2803 seek_count: u32::MAX,
2804 storage_cost: StorageCost {
2805 added_bytes: u32::MAX,
2806 replaced_bytes: u32::MAX,
2807 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(u32::MAX),
2808 },
2809 storage_loaded_bytes: u64::MAX,
2810 hash_node_calls: u32::MAX,
2811 sinsemilla_hash_calls: u32::MAX,
2812 };
2813 let result = cost.ephemeral_cost(fv);
2814 assert!(
2815 result.is_err(),
2816 "expected overflow error when summing large components"
2817 );
2818 }
2819}