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::drive::document::index_level_tree_types::{
19 zero_contribution_wrapper, ZeroContributionRefusal, ZeroContributionWrapper,
20};
21use crate::error::drive::DriveError;
22use crate::error::fee::FeeError;
23use crate::error::Error;
24use crate::fees::get_overflow_error;
25use crate::fees::op::LowLevelDriveOperation::{
26 CalculatedCostOperation, CalculatedEphemeralCostOperation, EphemeralGroveOperation,
27 FunctionOperation, GroveOperation, PreCalculatedFeeResult, RepaidIdentityDebt,
28};
29use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods;
30#[cfg(test)]
31use crate::util::grove_operations::pending_grove_operations::count_copied_pending_grove_operations;
32use crate::util::storage_flags::StorageFlags;
33use dpp::block::epoch::Epoch;
34use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
35use dpp::fee::fee_result::refunds::FeeRefunds;
36use dpp::fee::fee_result::{FeeResult, LifetimeStorageFees};
37use dpp::fee::Credits;
38use platform_version::version::fee::FeeVersion;
39
40#[derive(Debug, Enum)]
42pub enum BaseOp {
43 Stop,
45 Add,
47 Mul,
49 Sub,
51 Div,
53 Sdiv,
55 Mod,
57 Smod,
59 Addmod,
61 Mulmod,
63 Signextend,
65 Lt,
67 Gt,
69 Slt,
71 Sgt,
73 Eq,
75 Iszero,
77 And,
79 Or,
81 Xor,
83 Not,
85 Byte,
87}
88
89impl BaseOp {
90 pub fn cost(&self) -> u64 {
92 match self {
93 BaseOp::Stop => 0,
94 BaseOp::Add => 12,
95 BaseOp::Mul => 20,
96 BaseOp::Sub => 12,
97 BaseOp::Div => 20,
98 BaseOp::Sdiv => 20,
99 BaseOp::Mod => 20,
100 BaseOp::Smod => 20,
101 BaseOp::Addmod => 32,
102 BaseOp::Mulmod => 32,
103 BaseOp::Signextend => 20,
104 BaseOp::Lt => 12,
105 BaseOp::Gt => 12,
106 BaseOp::Slt => 12,
107 BaseOp::Sgt => 12,
108 BaseOp::Eq => 12,
109 BaseOp::Iszero => 12,
110 BaseOp::And => 12,
111 BaseOp::Or => 12,
112 BaseOp::Xor => 12,
113 BaseOp::Not => 12,
114 BaseOp::Byte => 12,
115 }
116 }
117}
118
119#[derive(Debug, Enum, PartialEq, Eq)]
121pub enum HashFunction {
122 Sha256RipeMD160,
124 Sha256,
126 Sha256_2,
128 Blake3,
130}
131
132impl HashFunction {
133 fn block_size(&self) -> u16 {
134 match self {
135 HashFunction::Sha256 => 64,
136 HashFunction::Sha256_2 => 64,
137 HashFunction::Blake3 => 64,
138 HashFunction::Sha256RipeMD160 => 64,
139 }
140 }
141
142 fn rounds(&self) -> u16 {
143 match self {
144 HashFunction::Sha256 => 1,
145 HashFunction::Sha256_2 => 2,
146 HashFunction::Blake3 => 1,
147 HashFunction::Sha256RipeMD160 => 1,
148 }
149 }
150
151 fn block_cost(&self, fee_version: &FeeVersion) -> u64 {
152 match self {
153 HashFunction::Sha256 => fee_version.hashing.sha256_per_block,
154 HashFunction::Sha256_2 => fee_version.hashing.sha256_per_block,
155 HashFunction::Blake3 => fee_version.hashing.blake3_per_block,
156 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_per_block,
157 }
158 }
159
160 fn base_cost(&self, fee_version: &FeeVersion) -> u64 {
161 match self {
162 HashFunction::Sha256 => fee_version.hashing.single_sha256_base,
163 HashFunction::Sha256_2 => fee_version.hashing.single_sha256_base,
166 HashFunction::Blake3 => fee_version.hashing.blake3_base,
167 HashFunction::Sha256RipeMD160 => fee_version.hashing.sha256_ripe_md160_base,
168 }
169 }
170}
171
172#[derive(Debug, PartialEq, Eq)]
174pub struct FunctionOp {
175 pub(crate) hash: HashFunction,
177 pub(crate) rounds: u32,
179}
180
181impl FunctionOp {
182 fn cost(&self, fee_version: &FeeVersion) -> Credits {
184 let block_cost = (self.rounds as u64).saturating_mul(self.hash.block_cost(fee_version));
185 self.hash.base_cost(fee_version).saturating_add(block_cost)
186 }
187
188 pub fn new_with_round_count(hash: HashFunction, rounds: u32) -> Self {
191 FunctionOp { hash, rounds }
192 }
193
194 pub fn new_with_byte_count(hash: HashFunction, byte_count: u16) -> Self {
197 let blocks = byte_count / hash.block_size() + 1;
198 let rounds = blocks + hash.rounds() - 1;
199 FunctionOp {
200 hash,
201 rounds: rounds as u32,
202 }
203 }
204}
205
206#[allow(clippy::large_enum_variant)]
211#[derive(Debug, Eq, PartialEq)]
212pub enum LowLevelDriveOperation {
213 GroveOperation(QualifiedGroveDbOp),
215 EphemeralGroveOperation(QualifiedGroveDbOp, EphemeralPricing),
226 FunctionOperation(FunctionOp),
228 CalculatedCostOperation(OperationCost),
230 CalculatedEphemeralCostOperation(OperationCost, EphemeralPricing),
234 PreCalculatedFeeResult(FeeResult),
236 RepaidIdentityDebt(Credits),
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
252pub enum EphemeralPricing {
253 TimeRangeTtl,
258 DocumentTtl {
264 credit_per_byte: Credits,
266 lifetime_epochs: u16,
269 },
270}
271
272impl EphemeralPricing {
273 fn apply_rank(&self) -> u8 {
277 match self {
278 EphemeralPricing::DocumentTtl { .. } => 0,
279 EphemeralPricing::TimeRangeTtl => 1,
280 }
281 }
282}
283
284const INDEXED_INNER_UNWRAPPABLE: &str =
304 "an indexed tree cannot be wrapped in NonCounted / NotSummed / NotCountedOrSummed: the \
305 wrapper suppresses the subtree's contribution to its parent's aggregate, but an indexed \
306 primary commits its aggregate (and the derived secondary root keys) through that very \
307 parent element. A ranked index's terminal property-name tree therefore cannot live inside \
308 an aggregating value tree — i.e. a ranked compound index [a, b] cannot coexist with a \
309 countable/summable index terminating at [a]; contracts declaring that pair are rejected \
310 at parse time.";
311
312impl LowLevelDriveOperation {
313 pub fn consume_to_fees_v0(
316 drive_operations: Vec<LowLevelDriveOperation>,
317 epoch: &Epoch,
318 epochs_per_era: u16,
319 fee_version: &FeeVersion,
320 previous_fee_versions: Option<&CachedEpochIndexFeeVersions>,
321 ) -> Result<Vec<FeeResult>, Error> {
322 drive_operations
323 .into_iter()
324 .map(|operation| match operation {
325 PreCalculatedFeeResult(f) => Ok(f),
326 FunctionOperation(op) => Ok(FeeResult {
327 processing_fee: op.cost(fee_version),
328 ..Default::default()
329 }),
330 CalculatedEphemeralCostOperation(cost, EphemeralPricing::DocumentTtl {
331 credit_per_byte,
332 lifetime_epochs,
333 }) => {
334 let storage_fee = (cost.storage_cost.added_bytes as u64)
344 .checked_mul(credit_per_byte)
345 .ok_or(Error::Fee(FeeError::Overflow(
346 "overflow pricing the bytes of a document with a time to live",
347 )))?;
348 let processing_fee = cost.ephemeral_cost(fee_version)?;
349 let lifetime_storage_fees = if storage_fee > 0 {
350 LifetimeStorageFees::from([(lifetime_epochs, storage_fee)])
351 } else {
352 LifetimeStorageFees::new()
353 };
354 let removed_bytes_from_system =
360 cost.storage_cost.removed_bytes.total_removed_bytes();
361 Ok(FeeResult {
362 storage_fee,
363 processing_fee,
364 fee_refunds: FeeRefunds::default(),
365 removed_bytes_from_system,
366 lifetime_storage_fees,
367 })
368 }
369 CalculatedEphemeralCostOperation(cost, EphemeralPricing::TimeRangeTtl) => {
370 let ephemeral_bytes_fee = (cost.storage_cost.added_bytes as u64)
378 .checked_mul(
379 fee_version
380 .storage
381 .ttl_ephemeral_disk_usage_credit_per_byte,
382 )
383 .ok_or(Error::Fee(FeeError::Overflow(
384 "overflow pricing ephemeral bytes",
385 )))?;
386 let processing_fee = cost
387 .ephemeral_cost(fee_version)?
388 .checked_add(ephemeral_bytes_fee)
389 .ok_or(Error::Fee(FeeError::Overflow(
390 "overflow adding ephemeral bytes fee",
391 )))?;
392 let removed_bytes_from_system = match cost.storage_cost.removed_bytes {
393 NoStorageRemoval => 0,
394 BasicStorageRemoval(amount) => amount,
395 SectionedStorageRemoval(_) => {
396 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
397 "TTL'd subtrees carry no storage flags, so an ephemeral \
398 batch cannot produce sectioned (refundable) removal",
399 )))
400 }
401 };
402 Ok(FeeResult {
403 storage_fee: 0,
404 processing_fee,
405 fee_refunds: FeeRefunds::default(),
406 removed_bytes_from_system,
407 lifetime_storage_fees: Default::default(),
408 })
409 }
410 _ => {
411 let cost = operation.operation_cost()?;
412 let storage_fee = cost.storage_cost.added_bytes as u64 * fee_version.storage.storage_disk_usage_credit_per_byte;
415 let processing_fee = cost.ephemeral_cost(fee_version)?;
416 let (fee_refunds, removed_bytes_from_system) =
417 match cost.storage_cost.removed_bytes {
418 NoStorageRemoval => (FeeRefunds::default(), 0),
419 BasicStorageRemoval(amount) => {
420 (FeeRefunds::default(), amount)
422 }
423 SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => {
424
425 let system_amount = removal_per_epoch_by_identifier
426 .remove(&Identifier::default())
427 .map_or(0, |a| a.values().sum());
428 if fee_version.fee_version_number == 1 {
429 (
430 FeeRefunds::from_storage_removal(
431 removal_per_epoch_by_identifier,
432 epoch.index,
433 epochs_per_era,
434 &BTreeMap::default(),
435 )?,
436 system_amount,
437 )
438 } else {
439 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")))?;
440 (
441 FeeRefunds::from_storage_removal(
442 removal_per_epoch_by_identifier,
443 epoch.index,
444 epochs_per_era,
445 previous_fee_versions,
446 )?,
447 system_amount,
448 )
449 }
450 }
451 };
452 Ok(FeeResult {
453 storage_fee,
454 processing_fee,
455 fee_refunds,
456 removed_bytes_from_system,
457 lifetime_storage_fees: Default::default(),
458 })
459 }
460 })
461 .collect()
462 }
463
464 pub fn operation_cost(self) -> Result<OperationCost, Error> {
466 match self {
467 GroveOperation(_) | EphemeralGroveOperation(..) => {
468 Err(Error::Drive(DriveError::CorruptedCodeExecution(
469 "grove operations must be executed, not directly transformed to costs",
470 )))
471 }
472 CalculatedCostOperation(c) | CalculatedEphemeralCostOperation(c, _) => Ok(c),
473 PreCalculatedFeeResult(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
474 "pre calculated fees should not be requested by operation costs",
475 ))),
476 FunctionOperation(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
477 "function operations should not be requested by operation costs",
478 ))),
479 RepaidIdentityDebt(_) => Err(Error::Drive(DriveError::CorruptedCodeExecution(
480 "a repaid identity debt must be routed to the processing fee pool, not priced",
481 ))),
482 }
483 }
484
485 pub fn take_repaid_identity_debt(
488 operations: &mut Vec<LowLevelDriveOperation>,
489 ) -> Result<Credits, Error> {
490 let mut repaid: Credits = 0;
491 let mut overflowed = false;
492 operations.retain(|operation| match operation {
493 RepaidIdentityDebt(credits) => {
494 match repaid.checked_add(*credits) {
495 Some(total) => repaid = total,
496 None => overflowed = true,
497 }
498 false
499 }
500 _ => true,
501 });
502 if overflowed {
503 return Err(get_overflow_error("repaid identity debt overflow"));
504 }
505 Ok(repaid)
506 }
507
508 pub fn holds_repaid_identity_debt(operations: &[LowLevelDriveOperation]) -> bool {
511 operations
512 .iter()
513 .any(|operation| matches!(operation, RepaidIdentityDebt(_)))
514 }
515
516 pub fn combine_cost_operations(operations: &[LowLevelDriveOperation]) -> OperationCost {
518 let mut cost = OperationCost::default();
519 operations.iter().for_each(|op| {
520 if let CalculatedCostOperation(operation_cost) = op {
521 cost += operation_cost.clone()
522 }
523 });
524 cost
525 }
526
527 pub fn grovedb_operations_batch(
529 insert_operations: &[LowLevelDriveOperation],
530 ) -> GroveDbOpBatch {
531 let operations: Vec<QualifiedGroveDbOp> = insert_operations
532 .iter()
533 .filter_map(|op| match op {
534 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op, _) => {
535 Some(grovedb_op.clone())
536 }
537 _ => None,
538 })
539 .collect();
540 #[cfg(test)]
541 count_copied_pending_grove_operations(operations.len());
542 GroveDbOpBatch::from_operations(operations)
543 }
544
545 pub fn grovedb_operations_batch_consume(
551 insert_operations: Vec<LowLevelDriveOperation>,
552 ) -> GroveDbOpBatch {
553 let operations = insert_operations
554 .into_iter()
555 .filter_map(|op| match op {
556 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op, _) => {
557 Some(grovedb_op)
558 }
559 _ => None,
560 })
561 .collect();
562 GroveDbOpBatch::from_operations(operations)
563 }
564
565 pub fn grovedb_operations_batch_consume_with_leftovers(
572 insert_operations: Vec<LowLevelDriveOperation>,
573 ) -> (GroveDbOpBatch, Vec<LowLevelDriveOperation>) {
574 let mut grove_operations = vec![];
575 let mut other_operations = vec![];
576 for op in insert_operations {
577 match op {
578 GroveOperation(grovedb_op) => grove_operations.push(grovedb_op),
579 other => other_operations.push(other),
580 }
581 }
582 (
583 GroveDbOpBatch::from_operations(grove_operations),
584 other_operations,
585 )
586 }
587
588 pub fn grovedb_operations_batch_consume_split_ephemeral(
596 insert_operations: Vec<LowLevelDriveOperation>,
597 ) -> (
598 GroveDbOpBatch,
599 Vec<(EphemeralPricing, GroveDbOpBatch)>,
600 Vec<LowLevelDriveOperation>,
601 ) {
602 let mut grove_operations = vec![];
603 let mut ephemeral_groups: Vec<(EphemeralPricing, Vec<QualifiedGroveDbOp>)> = vec![];
604 let mut other_operations = vec![];
605 for op in insert_operations {
606 match op {
607 GroveOperation(grovedb_op) => grove_operations.push(grovedb_op),
608 EphemeralGroveOperation(grovedb_op, pricing) => {
609 match ephemeral_groups
610 .iter_mut()
611 .find(|(group_pricing, _)| *group_pricing == pricing)
612 {
613 Some((_, group)) => group.push(grovedb_op),
614 None => ephemeral_groups.push((pricing, vec![grovedb_op])),
615 }
616 }
617 other => other_operations.push(other),
618 }
619 }
620 ephemeral_groups.sort_by_key(|(pricing, _)| pricing.apply_rank());
631 (
632 GroveDbOpBatch::from_operations(grove_operations),
633 ephemeral_groups
634 .into_iter()
635 .map(|(pricing, operations)| (pricing, GroveDbOpBatch::from_operations(operations)))
636 .collect(),
637 other_operations,
638 )
639 }
640
641 pub fn retag_ephemeral(self) -> LowLevelDriveOperation {
644 self.retag_ephemeral_with(EphemeralPricing::TimeRangeTtl)
645 }
646
647 pub fn retag_document_ttl(self, pricing: EphemeralPricing) -> LowLevelDriveOperation {
653 match self {
654 GroveOperation(_) => self.retag_ephemeral_with(pricing),
655 other => other,
656 }
657 }
658
659 pub fn retag_ephemeral_with(self, pricing: EphemeralPricing) -> LowLevelDriveOperation {
665 match self {
666 GroveOperation(mut grovedb_op) => {
667 match &mut grovedb_op.op {
675 GroveOp::InsertWithKnownToNotAlreadyExist { element }
676 | GroveOp::InsertIfNotExists { element, .. }
677 | GroveOp::InsertOrReplace { element }
678 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element }
679 | GroveOp::Replace { element }
680 | GroveOp::ReplaceDontCheckForBackwardsReferences { element }
681 | GroveOp::Patch { element, .. }
682 | GroveOp::PatchDontCheckForBackwardsReferences { element, .. } => {
683 element.set_flags(None)
684 }
685 GroveOp::RefreshReference { flags, .. } => *flags = None,
686 _ => {}
687 }
688 EphemeralGroveOperation(grovedb_op, pricing)
689 }
690 CalculatedCostOperation(cost) => CalculatedEphemeralCostOperation(cost, pricing),
691 other => other,
692 }
693 }
694
695 pub fn grovedb_operations_consume(
700 insert_operations: Vec<LowLevelDriveOperation>,
701 ) -> Vec<QualifiedGroveDbOp> {
702 insert_operations
703 .into_iter()
704 .filter_map(|op| match op {
705 GroveOperation(grovedb_op) | EphemeralGroveOperation(grovedb_op, _) => {
706 Some(grovedb_op)
707 }
708 _ => None,
709 })
710 .collect()
711 }
712
713 pub fn for_known_path_key_empty_tree(
715 path: Vec<Vec<u8>>,
716 key: Vec<u8>,
717 storage_flags: Option<&StorageFlags>,
718 ) -> Self {
719 let tree = match storage_flags {
720 Some(storage_flags) => {
721 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
722 }
723 None => Element::empty_tree(),
724 };
725
726 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
727 }
728
729 pub fn for_known_path_key_empty_sum_tree(
731 path: Vec<Vec<u8>>,
732 key: Vec<u8>,
733 storage_flags: Option<&StorageFlags>,
734 ) -> Self {
735 let tree = match storage_flags {
736 Some(storage_flags) => {
737 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
738 }
739 None => Element::empty_sum_tree(),
740 };
741
742 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
743 }
744
745 pub fn for_known_path_key_empty_big_sum_tree(
747 path: Vec<Vec<u8>>,
748 key: Vec<u8>,
749 storage_flags: Option<&StorageFlags>,
750 ) -> Self {
751 let tree = match storage_flags {
752 Some(storage_flags) => {
753 Element::new_big_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
754 }
755 None => Element::empty_big_sum_tree(),
756 };
757
758 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
759 }
760
761 pub fn for_known_path_key_empty_count_tree(
763 path: Vec<Vec<u8>>,
764 key: Vec<u8>,
765 storage_flags: Option<&StorageFlags>,
766 ) -> Self {
767 let tree = match storage_flags {
768 Some(storage_flags) => {
769 Element::new_count_tree_with_flags(None, storage_flags.to_some_element_flags())
770 }
771 None => Element::empty_count_tree(),
772 };
773
774 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
775 }
776
777 pub fn for_known_path_key_empty_count_sum_tree(
779 path: Vec<Vec<u8>>,
780 key: Vec<u8>,
781 storage_flags: Option<&StorageFlags>,
782 ) -> Self {
783 let tree = match storage_flags {
784 Some(storage_flags) => {
785 Element::new_count_sum_tree_with_flags(None, storage_flags.to_some_element_flags())
786 }
787 None => Element::new_count_sum_tree(None),
788 };
789
790 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
791 }
792
793 pub fn for_known_path_key_empty_non_counted_normal_tree(
801 path: Vec<Vec<u8>>,
802 key: Vec<u8>,
803 storage_flags: Option<&StorageFlags>,
804 ) -> Self {
805 Self::for_known_path_key_empty_non_counted_tree(
806 path,
807 key,
808 TreeType::NormalTree,
809 storage_flags,
810 )
811 .expect("NormalTree NonCounted wrapping never fails")
812 }
813
814 pub fn for_known_path_key_empty_non_counted_tree(
831 path: Vec<Vec<u8>>,
832 key: Vec<u8>,
833 tree_type: TreeType,
834 storage_flags: Option<&StorageFlags>,
835 ) -> Result<Self, Error> {
836 let element_flags = storage_flags.map(|s| s.to_element_flags());
843 let inner = match tree_type {
844 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
845 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
846 TreeType::ProvableCountTree => {
847 Element::empty_provable_count_tree_with_flags(element_flags)
848 }
849 TreeType::ProvableSumIndexedTree
850 | TreeType::ProvableCountIndexedTree
851 | TreeType::ProvableCountProvableSumIndexedTree => {
852 return Err(Error::Drive(DriveError::NotSupported(
853 INDEXED_INNER_UNWRAPPABLE,
854 )));
855 }
856 _ => {
857 return Err(Error::Drive(DriveError::NotSupported(
858 "NonCounted-wrapping is only supported for NormalTree, CountTree, and \
859 ProvableCountTree. For sum-bearing continuations under a sum or \
860 count+sum parent, use `for_known_path_key_empty_not_summed_tree` or \
861 `for_known_path_key_empty_not_counted_or_summed_tree` instead.",
862 )));
863 }
864 };
865 let tree = Element::new_non_counted(inner)?;
877 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
878 path, key, tree,
879 ))
880 }
881
882 pub fn for_known_path_key_empty_not_summed_tree(
891 path: Vec<Vec<u8>>,
892 key: Vec<u8>,
893 tree_type: TreeType,
894 storage_flags: Option<&StorageFlags>,
895 ) -> Result<Self, Error> {
896 let element_flags = storage_flags.map(|s| s.to_element_flags());
897 let inner = match tree_type {
898 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
899 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
900 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
901 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
902 TreeType::ProvableCountSumTree => {
903 Element::empty_provable_count_sum_tree_with_flags(element_flags)
904 }
905 TreeType::ProvableCountProvableSumTree => {
906 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
907 }
908 TreeType::ProvableSumIndexedTree
909 | TreeType::ProvableCountIndexedTree
910 | TreeType::ProvableCountProvableSumIndexedTree => {
911 return Err(Error::Drive(DriveError::NotSupported(
912 INDEXED_INNER_UNWRAPPABLE,
913 )));
914 }
915 _ => {
916 return Err(Error::Drive(DriveError::NotSupported(
917 "NotSummed-wrapping is only supported for the six sum-bearing tree \
918 variants (SumTree, BigSumTree, ProvableSumTree, CountSumTree, \
919 ProvableCountSumTree, ProvableCountProvableSumTree).",
920 )));
921 }
922 };
923 let tree = Element::new_not_summed(inner).map_err(|_| {
924 Error::Drive(DriveError::NotSupported(
925 "Element::new_not_summed rejected the inner tree (unreachable given the \
926 match above).",
927 ))
928 })?;
929 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
930 path, key, tree,
931 ))
932 }
933
934 pub fn wrap_in_non_aggregated_for_parent_tree_type(
963 path: Vec<Vec<u8>>,
964 key: Vec<u8>,
965 aggregating_parent_tree_type: TreeType,
966 inner_tree_type: TreeType,
967 storage_flags: Option<&StorageFlags>,
968 ) -> Result<Self, Error> {
969 match aggregating_parent_tree_type {
970 TreeType::CountTree
979 | TreeType::ProvableCountTree
980 | TreeType::ProvableCountIndexedTree => {
981 Self::for_known_path_key_empty_non_counted_tree(
982 path,
983 key,
984 inner_tree_type,
985 storage_flags,
986 )
987 }
988 TreeType::SumTree
992 | TreeType::BigSumTree
993 | TreeType::ProvableSumTree
994 | TreeType::ProvableSumIndexedTree => Self::for_known_path_key_empty_not_summed_tree(
995 path,
996 key,
997 inner_tree_type,
998 storage_flags,
999 ),
1000 TreeType::CountSumTree
1003 | TreeType::ProvableCountSumTree
1004 | TreeType::ProvableCountProvableSumTree
1005 | TreeType::ProvableCountProvableSumIndexedTree => {
1006 Self::for_known_path_key_empty_not_counted_or_summed_tree(
1007 path,
1008 key,
1009 inner_tree_type,
1010 storage_flags,
1011 )
1012 }
1013 _ => Err(Error::Drive(DriveError::NotSupported(
1014 "wrap_in_non_aggregated_for_parent_tree_type called with a non-aggregating \
1015 parent tree type — caller should use the unwrapped \
1016 `empty_tree_operation_for_known_path_key` path instead.",
1017 ))),
1018 }
1019 }
1020
1021 pub fn for_known_path_key_empty_not_counted_or_summed_tree(
1029 path: Vec<Vec<u8>>,
1030 key: Vec<u8>,
1031 tree_type: TreeType,
1032 storage_flags: Option<&StorageFlags>,
1033 ) -> Result<Self, Error> {
1034 let element_flags = storage_flags.map(|s| s.to_element_flags());
1035 let inner = match tree_type {
1036 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1037 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1038 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1039 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1040 TreeType::ProvableCountSumTree => {
1041 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1042 }
1043 TreeType::ProvableCountProvableSumTree => {
1044 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1045 }
1046 TreeType::ProvableSumIndexedTree
1047 | TreeType::ProvableCountIndexedTree
1048 | TreeType::ProvableCountProvableSumIndexedTree => {
1049 return Err(Error::Drive(DriveError::NotSupported(
1050 INDEXED_INNER_UNWRAPPABLE,
1051 )));
1052 }
1053 _ => {
1054 return Err(Error::Drive(DriveError::NotSupported(
1055 "NotCountedOrSummed-wrapping is only supported for the six sum-bearing \
1056 tree variants — see `for_known_path_key_empty_not_summed_tree`.",
1057 )));
1058 }
1059 };
1060 let tree = Element::new_not_counted_or_summed(inner).map_err(|_| {
1061 Error::Drive(DriveError::NotSupported(
1062 "Element::new_not_counted_or_summed rejected the inner tree (unreachable \
1063 given the match above).",
1064 ))
1065 })?;
1066 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1067 path, key, tree,
1068 ))
1069 }
1070
1071 pub fn for_known_path_key_empty_tree_contributing_zero_to_parent(
1110 path: Vec<Vec<u8>>,
1111 key: Vec<u8>,
1112 aggregating_parent_tree_type: TreeType,
1113 inner_tree_type: TreeType,
1114 storage_flags: Option<&StorageFlags>,
1115 ) -> Result<Self, Error> {
1116 match zero_contribution_wrapper(aggregating_parent_tree_type, inner_tree_type) {
1120 Ok(Some(ZeroContributionWrapper::NonCounted)) => {
1121 Self::for_known_path_key_empty_non_counted_any_tree(
1122 path,
1123 key,
1124 inner_tree_type,
1125 storage_flags,
1126 )
1127 }
1128 Ok(Some(ZeroContributionWrapper::NotCountedOrSummed)) => {
1129 Self::for_known_path_key_empty_not_counted_or_summed_tree(
1130 path,
1131 key,
1132 inner_tree_type,
1133 storage_flags,
1134 )
1135 }
1136 Ok(Some(ZeroContributionWrapper::NotSummed)) => {
1137 Self::for_known_path_key_empty_not_summed_tree(
1138 path,
1139 key,
1140 inner_tree_type,
1141 storage_flags,
1142 )
1143 }
1144 Ok(None) => {
1145 inner_tree_type.empty_tree_operation_for_known_path_key(path, key, storage_flags)
1146 }
1147 Err(ZeroContributionRefusal::IndexedInner) => Err(Error::Drive(
1156 DriveError::NotSupported(INDEXED_INNER_UNWRAPPABLE),
1157 )),
1158 Err(ZeroContributionRefusal::IndexedParent) => {
1168 Err(Error::Drive(DriveError::NotSupported(
1169 "indexed trees are property-name trees, never value trees, so they cannot \
1170 host zero-contributing continuation children — see \
1171 crate::drive::document::ranked_index_tree_type.",
1172 )))
1173 }
1174 Err(ZeroContributionRefusal::ProvableCountParent) => {
1175 Err(Error::Drive(DriveError::NotSupported(
1176 "provable count-bearing parents cannot host zero-contributing children — \
1177 grovedb commits their count into every node hash and rejects NonCounted / \
1178 NotCountedOrSummed children; the index walker must demote such value trees \
1179 to CountSumTree before hanging continuations under them (see \
1180 index_level_tree_types_with_continuation_demotion).",
1181 )))
1182 }
1183 Err(ZeroContributionRefusal::NonAggregatingParent) => {
1184 Err(Error::Drive(DriveError::NotSupported(
1185 "for_known_path_key_empty_tree_contributing_zero_to_parent called with a \
1186 non-aggregating parent tree type — caller should use the unwrapped \
1187 `empty_tree_operation_for_known_path_key` path instead.",
1188 )))
1189 }
1190 }
1191 }
1192
1193 pub fn for_known_path_key_empty_non_counted_any_tree(
1210 path: Vec<Vec<u8>>,
1211 key: Vec<u8>,
1212 tree_type: TreeType,
1213 storage_flags: Option<&StorageFlags>,
1214 ) -> Result<Self, Error> {
1215 let element_flags = storage_flags.map(|s| s.to_element_flags());
1216 let inner = match tree_type {
1217 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1218 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1219 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1220 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1221 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1222 TreeType::ProvableCountTree => {
1223 Element::empty_provable_count_tree_with_flags(element_flags)
1224 }
1225 TreeType::ProvableCountSumTree => {
1226 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1227 }
1228 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1229 TreeType::ProvableCountProvableSumTree => {
1230 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1231 }
1232 TreeType::ProvableSumIndexedTree
1233 | TreeType::ProvableCountIndexedTree
1234 | TreeType::ProvableCountProvableSumIndexedTree => {
1235 return Err(Error::Drive(DriveError::NotSupported(
1236 INDEXED_INNER_UNWRAPPABLE,
1237 )));
1238 }
1239 _ => {
1240 return Err(Error::Drive(DriveError::NotSupported(
1241 "NonCounted-wrapping is only supported for the nine standard merk tree \
1242 variants; special trees (commitment / MMR / bulk-append / dense) are \
1243 never index continuation trees.",
1244 )));
1245 }
1246 };
1247 let tree = Element::new_non_counted(inner)?;
1248 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1249 path, key, tree,
1250 ))
1251 }
1252
1253 pub fn for_known_path_key_empty_provable_count_tree(
1255 path: Vec<Vec<u8>>,
1256 key: Vec<u8>,
1257 storage_flags: Option<&StorageFlags>,
1258 ) -> Self {
1259 let tree = match storage_flags {
1260 Some(storage_flags) => Element::new_provable_count_tree_with_flags(
1261 None,
1262 storage_flags.to_some_element_flags(),
1263 ),
1264 None => Element::empty_provable_count_tree(),
1265 };
1266
1267 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1268 }
1269
1270 pub fn for_known_path_key_empty_provable_sum_tree(
1280 path: Vec<Vec<u8>>,
1281 key: Vec<u8>,
1282 storage_flags: Option<&StorageFlags>,
1283 ) -> Self {
1284 let tree = match storage_flags {
1285 Some(storage_flags) => Element::new_provable_sum_tree_with_flags(
1286 None,
1287 storage_flags.to_some_element_flags(),
1288 ),
1289 None => Element::empty_provable_sum_tree(),
1290 };
1291
1292 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1293 }
1294
1295 pub fn for_known_path_key_empty_provable_count_sum_tree(
1304 path: Vec<Vec<u8>>,
1305 key: Vec<u8>,
1306 storage_flags: Option<&StorageFlags>,
1307 ) -> Self {
1308 let tree = match storage_flags {
1309 Some(storage_flags) => Element::new_provable_count_sum_tree_with_flags(
1310 None,
1311 storage_flags.to_some_element_flags(),
1312 ),
1313 None => Element::empty_provable_count_sum_tree(),
1314 };
1315
1316 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1317 }
1318
1319 pub fn for_known_path_key_empty_provable_count_provable_sum_tree(
1335 path: Vec<Vec<u8>>,
1336 key: Vec<u8>,
1337 storage_flags: Option<&StorageFlags>,
1338 ) -> Self {
1339 let tree = match storage_flags {
1340 Some(storage_flags) => Element::new_provable_count_provable_sum_tree_with_flags(
1341 None,
1342 storage_flags.to_some_element_flags(),
1343 ),
1344 None => Element::empty_provable_count_provable_sum_tree(),
1345 };
1346
1347 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1348 }
1349
1350 pub fn for_estimated_path_key_empty_tree(
1352 path: KeyInfoPath,
1353 key: KeyInfo,
1354 storage_flags: Option<&StorageFlags>,
1355 ) -> Self {
1356 let tree = match storage_flags {
1357 Some(storage_flags) => {
1358 Element::empty_tree_with_flags(storage_flags.to_some_element_flags())
1359 }
1360 None => Element::empty_tree(),
1361 };
1362
1363 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1364 }
1365
1366 pub fn for_estimated_path_key_empty_sum_tree(
1368 path: KeyInfoPath,
1369 key: KeyInfo,
1370 storage_flags: Option<&StorageFlags>,
1371 ) -> Self {
1372 let tree = match storage_flags {
1373 Some(storage_flags) => {
1374 Element::empty_sum_tree_with_flags(storage_flags.to_some_element_flags())
1375 }
1376 None => Element::empty_sum_tree(),
1377 };
1378
1379 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1380 }
1381
1382 pub fn for_estimated_path_key_empty_count_tree(
1384 path: KeyInfoPath,
1385 key: KeyInfo,
1386 storage_flags: Option<&StorageFlags>,
1387 ) -> Self {
1388 let tree = match storage_flags {
1389 Some(storage_flags) => {
1390 Element::empty_count_tree_with_flags(storage_flags.to_some_element_flags())
1391 }
1392 None => Element::empty_count_tree(),
1393 };
1394
1395 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1396 }
1397
1398 pub fn for_estimated_path_key_empty_provable_count_tree(
1400 path: KeyInfoPath,
1401 key: KeyInfo,
1402 storage_flags: Option<&StorageFlags>,
1403 ) -> Self {
1404 let tree = match storage_flags {
1405 Some(storage_flags) => {
1406 Element::empty_provable_count_tree_with_flags(storage_flags.to_some_element_flags())
1407 }
1408 None => Element::empty_provable_count_tree(),
1409 };
1410
1411 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1412 }
1413
1414 pub fn for_estimated_path_key_empty_provable_sum_tree(
1417 path: KeyInfoPath,
1418 key: KeyInfo,
1419 storage_flags: Option<&StorageFlags>,
1420 ) -> Self {
1421 let tree = match storage_flags {
1422 Some(storage_flags) => {
1423 Element::empty_provable_sum_tree_with_flags(storage_flags.to_some_element_flags())
1424 }
1425 None => Element::empty_provable_sum_tree(),
1426 };
1427
1428 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1429 }
1430
1431 pub fn for_estimated_path_key_empty_count_sum_tree(
1434 path: KeyInfoPath,
1435 key: KeyInfo,
1436 storage_flags: Option<&StorageFlags>,
1437 ) -> Self {
1438 let tree = match storage_flags {
1439 Some(storage_flags) => {
1440 Element::empty_count_sum_tree_with_flags(storage_flags.to_some_element_flags())
1441 }
1442 None => Element::empty_count_sum_tree(),
1443 };
1444
1445 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1446 }
1447
1448 pub fn for_estimated_path_key_empty_provable_count_sum_tree(
1452 path: KeyInfoPath,
1453 key: KeyInfo,
1454 storage_flags: Option<&StorageFlags>,
1455 ) -> Self {
1456 let tree = match storage_flags {
1457 Some(storage_flags) => Element::empty_provable_count_sum_tree_with_flags(
1458 storage_flags.to_some_element_flags(),
1459 ),
1460 None => Element::empty_provable_count_sum_tree(),
1461 };
1462
1463 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1464 }
1465
1466 pub fn for_estimated_path_key_empty_provable_count_provable_sum_tree(
1470 path: KeyInfoPath,
1471 key: KeyInfo,
1472 storage_flags: Option<&StorageFlags>,
1473 ) -> Self {
1474 let tree = match storage_flags {
1475 Some(storage_flags) => Element::empty_provable_count_provable_sum_tree_with_flags(
1476 storage_flags.to_some_element_flags(),
1477 ),
1478 None => Element::empty_provable_count_provable_sum_tree(),
1479 };
1480
1481 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1482 }
1483
1484 pub fn for_known_path_key_empty_provable_count_indexed_tree(
1501 path: Vec<Vec<u8>>,
1502 key: Vec<u8>,
1503 storage_flags: Option<&StorageFlags>,
1504 ) -> Self {
1505 let tree = match storage_flags {
1506 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1507 storage_flags.to_some_element_flags(),
1508 ),
1509 None => Element::empty_provable_count_indexed_tree(),
1510 };
1511
1512 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1513 }
1514
1515 pub fn for_known_path_key_empty_provable_sum_indexed_tree(
1521 path: Vec<Vec<u8>>,
1522 key: Vec<u8>,
1523 storage_flags: Option<&StorageFlags>,
1524 ) -> Self {
1525 let tree = match storage_flags {
1526 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1527 storage_flags.to_some_element_flags(),
1528 ),
1529 None => Element::empty_provable_sum_indexed_tree(),
1530 };
1531
1532 LowLevelDriveOperation::insert_for_known_path_key_element(path, key, tree)
1533 }
1534
1535 pub fn for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1548 path: Vec<Vec<u8>>,
1549 key: Vec<u8>,
1550 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1551 storage_flags: Option<&StorageFlags>,
1552 ) -> Result<Self, Error> {
1553 let tree = match storage_flags {
1554 Some(storage_flags) => {
1555 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1556 ranked_axes,
1557 storage_flags.to_some_element_flags(),
1558 )?
1559 }
1560 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1561 };
1562
1563 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1564 path, key, tree,
1565 ))
1566 }
1567
1568 pub fn for_known_path_key_empty_indexed_tree(
1578 path: Vec<Vec<u8>>,
1579 key: Vec<u8>,
1580 tree_type: TreeType,
1581 ranked_axes: &[IndexAxis],
1582 storage_flags: Option<&StorageFlags>,
1583 ) -> Result<Self, Error> {
1584 match (tree_type, ranked_axes) {
1585 (TreeType::ProvableCountIndexedTree, [IndexAxis::Count]) => {
1586 Ok(Self::for_known_path_key_empty_provable_count_indexed_tree(
1587 path,
1588 key,
1589 storage_flags,
1590 ))
1591 }
1592 (TreeType::ProvableSumIndexedTree, [IndexAxis::Sum]) => Ok(
1593 Self::for_known_path_key_empty_provable_sum_indexed_tree(path, key, storage_flags),
1594 ),
1595 (TreeType::ProvableCountProvableSumIndexedTree, axes) if !axes.is_empty() => {
1596 Self::for_known_path_key_empty_provable_count_provable_sum_indexed_tree(
1597 path,
1598 key,
1599 axes.iter().map(|axis| (axis.tag(), None)).collect(),
1600 storage_flags,
1601 )
1602 }
1603 _ => Err(Error::Drive(DriveError::NotSupported(
1604 "for_known_path_key_empty_indexed_tree called with a tree type / ranked-axis \
1605 pair that does not describe an indexed tree — the single-axis PCIT / PSIT \
1606 variants accept exactly their own axis and PCPSIT needs a non-empty axis list.",
1607 ))),
1608 }
1609 }
1610
1611 pub fn for_estimated_path_key_empty_provable_count_indexed_tree(
1614 path: KeyInfoPath,
1615 key: KeyInfo,
1616 storage_flags: Option<&StorageFlags>,
1617 ) -> Self {
1618 let tree = match storage_flags {
1619 Some(storage_flags) => Element::empty_provable_count_indexed_tree_with_flags(
1620 storage_flags.to_some_element_flags(),
1621 ),
1622 None => Element::empty_provable_count_indexed_tree(),
1623 };
1624
1625 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1626 }
1627
1628 pub fn for_estimated_path_key_empty_provable_sum_indexed_tree(
1631 path: KeyInfoPath,
1632 key: KeyInfo,
1633 storage_flags: Option<&StorageFlags>,
1634 ) -> Self {
1635 let tree = match storage_flags {
1636 Some(storage_flags) => Element::empty_provable_sum_indexed_tree_with_flags(
1637 storage_flags.to_some_element_flags(),
1638 ),
1639 None => Element::empty_provable_sum_indexed_tree(),
1640 };
1641
1642 LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree)
1643 }
1644
1645 pub fn for_estimated_path_key_empty_provable_count_provable_sum_indexed_tree(
1648 path: KeyInfoPath,
1649 key: KeyInfo,
1650 ranked_axes: Vec<(u8, Option<Vec<u8>>)>,
1651 storage_flags: Option<&StorageFlags>,
1652 ) -> Result<Self, Error> {
1653 let tree = match storage_flags {
1654 Some(storage_flags) => {
1655 Element::empty_provable_count_provable_sum_indexed_tree_with_flags(
1656 ranked_axes,
1657 storage_flags.to_some_element_flags(),
1658 )?
1659 }
1660 None => Element::empty_provable_count_provable_sum_indexed_tree(ranked_axes)?,
1661 };
1662
1663 Ok(LowLevelDriveOperation::insert_for_estimated_path_key_element(path, key, tree))
1664 }
1665
1666 pub fn insert_for_known_path_key_element(
1668 path: Vec<Vec<u8>>,
1669 key: Vec<u8>,
1670 element: Element,
1671 ) -> Self {
1672 GroveOperation(
1673 QualifiedGroveDbOp::insert_or_replace_op(path, key, element)
1674 .dont_check_for_backwards_references(),
1675 )
1676 }
1677
1678 pub fn replace_for_known_path_key_element(
1680 path: Vec<Vec<u8>>,
1681 key: Vec<u8>,
1682 element: Element,
1683 ) -> Self {
1684 GroveOperation(
1685 QualifiedGroveDbOp::replace_op(path, key, element)
1686 .dont_check_for_backwards_references(),
1687 )
1688 }
1689
1690 pub fn patch_for_known_path_key_element(
1693 path: Vec<Vec<u8>>,
1694 key: Vec<u8>,
1695 element: Element,
1696 change_in_bytes: i32,
1697 ) -> Self {
1698 GroveOperation(
1699 QualifiedGroveDbOp::patch_op(path, key, element, change_in_bytes)
1700 .dont_check_for_backwards_references(),
1701 )
1702 }
1703
1704 pub fn insert_for_estimated_path_key_element(
1706 path: KeyInfoPath,
1707 key: KeyInfo,
1708 element: Element,
1709 ) -> Self {
1710 GroveOperation(
1711 QualifiedGroveDbOp::insert_estimated_op(path, key, element)
1712 .dont_check_for_backwards_references(),
1713 )
1714 }
1715
1716 pub fn replace_for_estimated_path_key_element(
1718 path: KeyInfoPath,
1719 key: KeyInfo,
1720 element: Element,
1721 ) -> Self {
1722 GroveOperation(
1723 QualifiedGroveDbOp::replace_estimated_op(path, key, element)
1724 .dont_check_for_backwards_references(),
1725 )
1726 }
1727
1728 pub fn refresh_reference_for_known_path_key_reference_info(
1730 path: Vec<Vec<u8>>,
1731 key: Vec<u8>,
1732 reference_path_type: ReferencePathType,
1733 max_reference_hop: MaxReferenceHop,
1734 flags: Option<ElementFlags>,
1735 trust_refresh_reference: bool,
1736 ) -> Self {
1737 GroveOperation(QualifiedGroveDbOp::refresh_reference_op(
1738 path,
1739 key,
1740 reference_path_type,
1741 max_reference_hop,
1742 flags,
1743 false,
1750 trust_refresh_reference,
1751 ))
1752 }
1753
1754 pub fn refresh_reference_with_sum_item_for_known_path_key_reference_info(
1768 path: Vec<Vec<u8>>,
1769 key: Vec<u8>,
1770 reference_path_type: ReferencePathType,
1771 max_reference_hop: MaxReferenceHop,
1772 sum_value: i64,
1773 flags: Option<ElementFlags>,
1774 trust_refresh_reference: bool,
1775 ) -> Self {
1776 GroveOperation(QualifiedGroveDbOp::refresh_reference_with_sum_item_op(
1777 path,
1778 key,
1779 reference_path_type,
1780 max_reference_hop,
1781 sum_value,
1782 flags,
1783 false,
1788 trust_refresh_reference,
1789 ))
1790 }
1791}
1792
1793pub trait LowLevelDriveOperationTreeTypeConverter {
1795 fn empty_tree_operation_for_known_path_key(
1797 &self,
1798 path: Vec<Vec<u8>>,
1799 key: Vec<u8>,
1800 storage_flags: Option<&StorageFlags>,
1801 ) -> Result<LowLevelDriveOperation, Error>;
1802}
1803
1804impl LowLevelDriveOperationTreeTypeConverter for TreeType {
1805 fn empty_tree_operation_for_known_path_key(
1807 &self,
1808 path: Vec<Vec<u8>>,
1809 key: Vec<u8>,
1810 storage_flags: Option<&StorageFlags>,
1811 ) -> Result<LowLevelDriveOperation, Error> {
1812 let element_flags = storage_flags.map(|storage_flags| storage_flags.to_element_flags());
1813 let element = match self {
1814 TreeType::NormalTree => Element::empty_tree_with_flags(element_flags),
1815 TreeType::SumTree => Element::empty_sum_tree_with_flags(element_flags),
1816 TreeType::BigSumTree => Element::empty_big_sum_tree_with_flags(element_flags),
1817 TreeType::CountTree => Element::empty_count_tree_with_flags(element_flags),
1818 TreeType::CountSumTree => Element::empty_count_sum_tree_with_flags(element_flags),
1819 TreeType::ProvableCountTree => {
1820 Element::empty_provable_count_tree_with_flags(element_flags)
1821 }
1822 TreeType::ProvableCountSumTree => {
1823 Element::empty_provable_count_sum_tree_with_flags(element_flags)
1824 }
1825 TreeType::ProvableCountProvableSumTree => {
1826 Element::empty_provable_count_provable_sum_tree_with_flags(element_flags)
1827 }
1828 TreeType::ProvableSumTree => Element::empty_provable_sum_tree_with_flags(element_flags),
1829 TreeType::CommitmentTree(chunk_power) => {
1830 Element::empty_commitment_tree_with_flags(*chunk_power, element_flags)?
1831 }
1832 TreeType::MmrTree => Element::empty_mmr_tree_with_flags(element_flags),
1833 TreeType::BulkAppendTree(chunk_power) => {
1834 Element::empty_bulk_append_tree_with_flags(*chunk_power, element_flags)?
1835 }
1836 TreeType::DenseAppendOnlyFixedSizeTree(chunk_power) => {
1837 Element::empty_dense_tree_with_flags(*chunk_power, element_flags)
1838 }
1839 TreeType::ProvableSumIndexedTree => {
1844 Element::empty_provable_sum_indexed_tree_with_flags(element_flags)
1845 }
1846 TreeType::ProvableCountIndexedTree => {
1847 Element::empty_provable_count_indexed_tree_with_flags(element_flags)
1848 }
1849 TreeType::ProvableCountProvableSumIndexedTree => {
1861 return Err(Error::Drive(DriveError::NotSupported(
1862 "empty_tree_operation_for_known_path_key cannot create a \
1863 ProvableCountProvableSumIndexedTree — the ranked axis set is not carried \
1864 by TreeType; use for_known_path_key_empty_indexed_tree (or \
1865 batch_insert_empty_provable_count_provable_sum_indexed_tree) instead.",
1866 )))
1867 }
1868 TreeType::PrivateDocumentStore(_) => {
1874 return Err(Error::Drive(DriveError::NotSupported(
1875 "empty_tree_operation_for_known_path_key cannot create a \
1876 PrivateDocumentStore — the entry size is not carried by TreeType",
1877 )))
1878 }
1879 };
1880
1881 Ok(LowLevelDriveOperation::insert_for_known_path_key_element(
1882 path, key, element,
1883 ))
1884 }
1885}
1886
1887pub trait DriveCost {
1889 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<u64, Error>;
1891}
1892
1893impl DriveCost for OperationCost {
1894 fn ephemeral_cost(&self, fee_version: &FeeVersion) -> Result<Credits, Error> {
1896 let OperationCost {
1897 seek_count,
1898 storage_cost,
1899 storage_loaded_bytes,
1900 hash_node_calls,
1901 sinsemilla_hash_calls,
1902 } = self;
1903 let epoch_cost_for_processing_credit_per_byte =
1904 fee_version.storage.storage_processing_credit_per_byte;
1905 let seek_cost = (*seek_count as u64)
1906 .checked_mul(fee_version.storage.storage_seek_cost)
1907 .ok_or_else(|| get_overflow_error("seek cost overflow"))?;
1908 let storage_added_bytes_ephemeral_cost = (storage_cost.added_bytes as u64)
1909 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1910 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1911 let storage_replaced_bytes_ephemeral_cost = (storage_cost.replaced_bytes as u64)
1912 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1913 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1914 let storage_removed_bytes_ephemeral_cost =
1915 (storage_cost.removed_bytes.total_removed_bytes() as u64)
1916 .checked_mul(epoch_cost_for_processing_credit_per_byte)
1917 .ok_or_else(|| get_overflow_error("storage written bytes cost overflow"))?;
1918 let storage_loaded_bytes_cost = { *storage_loaded_bytes }
1920 .checked_mul(fee_version.storage.storage_load_credit_per_byte)
1921 .ok_or_else(|| get_overflow_error("storage loaded cost overflow"))?;
1922
1923 let blake3_total = fee_version.hashing.blake3_base + fee_version.hashing.blake3_per_block;
1925 let hash_node_cost = blake3_total * (*hash_node_calls as u64);
1927 let sinsemilla_cost = fee_version.hashing.sinsemilla_base * (*sinsemilla_hash_calls as u64);
1928 seek_cost
1929 .checked_add(storage_added_bytes_ephemeral_cost)
1930 .and_then(|c| c.checked_add(storage_replaced_bytes_ephemeral_cost))
1931 .and_then(|c| c.checked_add(storage_loaded_bytes_cost))
1932 .and_then(|c| c.checked_add(storage_removed_bytes_ephemeral_cost))
1933 .and_then(|c| c.checked_add(hash_node_cost))
1934 .and_then(|c| c.checked_add(sinsemilla_cost))
1935 .ok_or_else(|| get_overflow_error("ephemeral cost addition overflow"))
1936 }
1937}
1938
1939#[cfg(test)]
1940#[allow(clippy::identity_op)]
1941mod tests {
1942 use super::*;
1943 use grovedb_costs::storage_cost::removal::StorageRemovedBytes;
1944 use grovedb_costs::storage_cost::StorageCost;
1945 use platform_version::version::fee::storage::FeeStorageVersion;
1946 use platform_version::version::fee::FeeVersion;
1947
1948 fn fee_version() -> &'static FeeVersion {
1950 FeeVersion::first()
1951 }
1952
1953 #[test]
1958 fn base_op_stop_costs_zero() {
1959 assert_eq!(BaseOp::Stop.cost(), 0);
1960 }
1961
1962 #[test]
1963 fn base_op_add_costs_12() {
1964 assert_eq!(BaseOp::Add.cost(), 12);
1965 }
1966
1967 #[test]
1968 fn base_op_mul_costs_20() {
1969 assert_eq!(BaseOp::Mul.cost(), 20);
1970 }
1971
1972 #[test]
1973 fn base_op_signextend_costs_20() {
1974 assert_eq!(BaseOp::Signextend.cost(), 20);
1975 }
1976
1977 #[test]
1978 fn base_op_addmod_costs_32() {
1979 assert_eq!(BaseOp::Addmod.cost(), 32);
1980 }
1981
1982 #[test]
1983 fn base_op_mulmod_costs_32() {
1984 assert_eq!(BaseOp::Mulmod.cost(), 32);
1985 }
1986
1987 #[test]
1988 fn base_op_byte_costs_12() {
1989 assert_eq!(BaseOp::Byte.cost(), 12);
1990 }
1991
1992 #[test]
1993 fn base_op_sub_costs_12() {
1994 assert_eq!(BaseOp::Sub.cost(), 12);
1995 }
1996
1997 #[test]
1998 fn base_op_div_costs_20() {
1999 assert_eq!(BaseOp::Div.cost(), 20);
2000 }
2001
2002 #[test]
2003 fn base_op_comparison_ops_all_cost_12() {
2004 for op in [
2005 BaseOp::Lt,
2006 BaseOp::Gt,
2007 BaseOp::Slt,
2008 BaseOp::Sgt,
2009 BaseOp::Eq,
2010 BaseOp::Iszero,
2011 ] {
2012 assert_eq!(op.cost(), 12, "comparison op {:?} should cost 12", op);
2013 }
2014 }
2015
2016 #[test]
2017 fn base_op_bitwise_ops_all_cost_12() {
2018 for op in [BaseOp::And, BaseOp::Or, BaseOp::Xor, BaseOp::Not] {
2019 assert_eq!(op.cost(), 12, "bitwise op {:?} should cost 12", op);
2020 }
2021 }
2022
2023 #[test]
2028 fn hash_function_block_size_all_64() {
2029 assert_eq!(HashFunction::Sha256.block_size(), 64);
2031 assert_eq!(HashFunction::Sha256_2.block_size(), 64);
2032 assert_eq!(HashFunction::Blake3.block_size(), 64);
2033 assert_eq!(HashFunction::Sha256RipeMD160.block_size(), 64);
2034 }
2035
2036 #[test]
2037 fn hash_function_rounds() {
2038 assert_eq!(HashFunction::Sha256.rounds(), 1);
2039 assert_eq!(HashFunction::Sha256_2.rounds(), 2);
2040 assert_eq!(HashFunction::Blake3.rounds(), 1);
2041 assert_eq!(HashFunction::Sha256RipeMD160.rounds(), 1);
2042 }
2043
2044 #[test]
2045 fn hash_function_block_cost_sha256_variants_use_sha256_per_block() {
2046 let fv = fee_version();
2047 let expected = fv.hashing.sha256_per_block;
2048 assert_eq!(HashFunction::Sha256.block_cost(fv), expected);
2049 assert_eq!(HashFunction::Sha256_2.block_cost(fv), expected);
2050 assert_eq!(HashFunction::Sha256RipeMD160.block_cost(fv), expected);
2051 }
2052
2053 #[test]
2054 fn hash_function_block_cost_blake3_uses_blake3_per_block() {
2055 let fv = fee_version();
2056 assert_eq!(
2057 HashFunction::Blake3.block_cost(fv),
2058 fv.hashing.blake3_per_block
2059 );
2060 }
2061
2062 #[test]
2063 fn hash_function_base_cost_sha256() {
2064 let fv = fee_version();
2065 assert_eq!(
2066 HashFunction::Sha256.base_cost(fv),
2067 fv.hashing.single_sha256_base
2068 );
2069 }
2070
2071 #[test]
2072 fn hash_function_base_cost_sha256_2_uses_single_sha256_base() {
2073 let fv = fee_version();
2074 assert_eq!(
2076 HashFunction::Sha256_2.base_cost(fv),
2077 fv.hashing.single_sha256_base
2078 );
2079 }
2080
2081 #[test]
2082 fn hash_function_base_cost_blake3() {
2083 let fv = fee_version();
2084 assert_eq!(HashFunction::Blake3.base_cost(fv), fv.hashing.blake3_base);
2085 }
2086
2087 #[test]
2088 fn hash_function_base_cost_sha256_ripe_md160() {
2089 let fv = fee_version();
2090 assert_eq!(
2091 HashFunction::Sha256RipeMD160.base_cost(fv),
2092 fv.hashing.sha256_ripe_md160_base
2093 );
2094 }
2095
2096 #[test]
2101 fn function_op_new_with_byte_count_small_sha256() {
2102 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 32);
2104 assert_eq!(op.rounds, 1);
2105 assert_eq!(op.hash, HashFunction::Sha256);
2106 }
2107
2108 #[test]
2109 fn function_op_new_with_byte_count_exact_block_boundary_sha256() {
2110 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 64);
2112 assert_eq!(op.rounds, 2);
2113 }
2114
2115 #[test]
2116 fn function_op_new_with_byte_count_large_sha256() {
2117 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 200);
2119 assert_eq!(op.rounds, 4);
2120 }
2121
2122 #[test]
2123 fn function_op_new_with_byte_count_sha256_2_has_extra_round() {
2124 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 32);
2126 assert_eq!(op.rounds, 2);
2127 }
2128
2129 #[test]
2130 fn function_op_new_with_byte_count_sha256_2_large() {
2131 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256_2, 200);
2133 assert_eq!(op.rounds, 5);
2134 }
2135
2136 #[test]
2137 fn function_op_new_with_byte_count_blake3_small() {
2138 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 10);
2140 assert_eq!(op.rounds, 1);
2141 assert_eq!(op.hash, HashFunction::Blake3);
2142 }
2143
2144 #[test]
2145 fn function_op_new_with_byte_count_blake3_large() {
2146 let op = FunctionOp::new_with_byte_count(HashFunction::Blake3, 500);
2148 assert_eq!(op.rounds, 8);
2149 }
2150
2151 #[test]
2152 fn function_op_new_with_byte_count_zero_bytes() {
2153 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 0);
2155 assert_eq!(op.rounds, 1);
2156 }
2157
2158 #[test]
2159 fn function_op_new_with_byte_count_sha256_ripemd160() {
2160 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256RipeMD160, 20);
2162 assert_eq!(op.rounds, 1);
2163 assert_eq!(op.hash, HashFunction::Sha256RipeMD160);
2164 }
2165
2166 #[test]
2171 fn function_op_cost_sha256_one_round() {
2172 let fv = fee_version();
2173 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 1);
2174 let expected = fv.hashing.single_sha256_base + 1 * fv.hashing.sha256_per_block;
2176 assert_eq!(op.cost(fv), expected);
2177 }
2178
2179 #[test]
2180 fn function_op_cost_sha256_2_two_rounds() {
2181 let fv = fee_version();
2182 let op = FunctionOp::new_with_round_count(HashFunction::Sha256_2, 2);
2183 let expected = fv.hashing.single_sha256_base + 2 * fv.hashing.sha256_per_block;
2185 assert_eq!(op.cost(fv), expected);
2186 }
2187
2188 #[test]
2189 fn function_op_cost_blake3_one_round() {
2190 let fv = fee_version();
2191 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 1);
2192 let expected = fv.hashing.blake3_base + 1 * fv.hashing.blake3_per_block;
2194 assert_eq!(op.cost(fv), expected);
2195 }
2196
2197 #[test]
2198 fn function_op_cost_zero_rounds() {
2199 let fv = fee_version();
2200 let op = FunctionOp::new_with_round_count(HashFunction::Blake3, 0);
2201 assert_eq!(op.cost(fv), fv.hashing.blake3_base);
2203 }
2204
2205 #[test]
2206 fn function_op_cost_from_byte_count_matches_manual_calc() {
2207 let fv = fee_version();
2208 let op = FunctionOp::new_with_byte_count(HashFunction::Sha256, 128);
2210 assert_eq!(op.rounds, 3);
2211 let expected = fv.hashing.single_sha256_base + 3 * fv.hashing.sha256_per_block;
2212 assert_eq!(op.cost(fv), expected);
2213 }
2214
2215 #[test]
2216 fn function_op_cost_sha256_ripemd160() {
2217 let fv = fee_version();
2218 let op = FunctionOp::new_with_round_count(HashFunction::Sha256RipeMD160, 1);
2219 let expected = fv.hashing.sha256_ripe_md160_base + 1 * fv.hashing.sha256_per_block;
2220 assert_eq!(op.cost(fv), expected);
2221 }
2222
2223 #[test]
2224 fn function_op_cost_saturating_mul_does_not_panic_on_large_rounds() {
2225 let fv = fee_version();
2226 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, u32::MAX);
2227 let expected_block_cost = (u32::MAX as u64).saturating_mul(fv.hashing.sha256_per_block);
2230 let expected = fv
2231 .hashing
2232 .single_sha256_base
2233 .saturating_add(expected_block_cost);
2234 assert_eq!(op.cost(fv), expected);
2235 }
2236
2237 #[test]
2238 fn function_op_cost_saturates_to_max_with_extreme_fee_version() {
2239 let mut fv = fee_version().clone();
2242 fv.hashing.sha256_per_block = u64::MAX;
2243 let op = FunctionOp::new_with_round_count(HashFunction::Sha256, 2);
2244 assert_eq!(op.cost(&fv), u64::MAX);
2246 }
2247
2248 #[test]
2253 fn operation_cost_calculated_cost_operation_returns_cost() {
2254 let cost = OperationCost {
2255 seek_count: 3,
2256 storage_cost: StorageCost {
2257 added_bytes: 100,
2258 replaced_bytes: 50,
2259 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2260 },
2261 storage_loaded_bytes: 200,
2262 hash_node_calls: 5,
2263 sinsemilla_hash_calls: 0,
2264 };
2265 let op = CalculatedCostOperation(cost.clone());
2266 let result = op.operation_cost().expect("should return Ok");
2267 assert_eq!(result, cost);
2268 }
2269
2270 #[test]
2271 fn operation_cost_grove_operation_returns_error() {
2272 let grove_op = LowLevelDriveOperation::insert_for_known_path_key_element(
2273 vec![vec![1, 2, 3]],
2274 vec![4, 5, 6],
2275 Element::empty_tree(),
2276 );
2277 let result = grove_op.operation_cost();
2278 assert!(result.is_err());
2279 let err_msg = format!("{:?}", result.unwrap_err());
2280 assert!(
2281 err_msg.contains("grove operations must be executed"),
2282 "unexpected error: {}",
2283 err_msg
2284 );
2285 }
2286
2287 #[test]
2288 fn operation_cost_pre_calculated_fee_result_returns_error() {
2289 let fee = FeeResult {
2290 storage_fee: 100,
2291 processing_fee: 200,
2292 ..Default::default()
2293 };
2294 let op = PreCalculatedFeeResult(fee);
2295 let result = op.operation_cost();
2296 assert!(result.is_err());
2297 let err_msg = format!("{:?}", result.unwrap_err());
2298 assert!(
2299 err_msg.contains("pre calculated fees should not be requested"),
2300 "unexpected error: {}",
2301 err_msg
2302 );
2303 }
2304
2305 #[test]
2306 fn operation_cost_function_operation_returns_error() {
2307 let func_op = FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1));
2308 let result = func_op.operation_cost();
2309 assert!(result.is_err());
2310 let err_msg = format!("{:?}", result.unwrap_err());
2311 assert!(
2312 err_msg.contains("function operations should not be requested"),
2313 "unexpected error: {}",
2314 err_msg
2315 );
2316 }
2317
2318 #[test]
2323 fn combine_cost_operations_sums_calculated_costs_only() {
2324 let cost1 = OperationCost {
2325 seek_count: 2,
2326 storage_cost: StorageCost {
2327 added_bytes: 10,
2328 replaced_bytes: 0,
2329 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2330 },
2331 storage_loaded_bytes: 50,
2332 hash_node_calls: 1,
2333 sinsemilla_hash_calls: 0,
2334 };
2335 let cost2 = OperationCost {
2336 seek_count: 3,
2337 storage_cost: StorageCost {
2338 added_bytes: 20,
2339 replaced_bytes: 5,
2340 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2341 },
2342 storage_loaded_bytes: 100,
2343 hash_node_calls: 2,
2344 sinsemilla_hash_calls: 1,
2345 };
2346
2347 let operations = vec![
2348 CalculatedCostOperation(cost1.clone()),
2349 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2351 CalculatedCostOperation(cost2.clone()),
2352 PreCalculatedFeeResult(FeeResult::default()),
2354 ];
2355
2356 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2357 assert_eq!(combined.seek_count, 2 + 3);
2358 assert_eq!(combined.storage_cost.added_bytes, 10 + 20);
2359 assert_eq!(combined.storage_cost.replaced_bytes, 0 + 5);
2360 assert_eq!(combined.storage_loaded_bytes, 50 + 100);
2361 assert_eq!(combined.hash_node_calls, 1 + 2);
2362 assert_eq!(combined.sinsemilla_hash_calls, 0 + 1);
2363 }
2364
2365 #[test]
2366 fn combine_cost_operations_empty_list_returns_default() {
2367 let combined = LowLevelDriveOperation::combine_cost_operations(&[]);
2368 assert_eq!(combined, OperationCost::default());
2369 }
2370
2371 #[test]
2372 fn combine_cost_operations_no_calculated_costs_returns_default() {
2373 let operations = vec![
2374 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 2)),
2375 PreCalculatedFeeResult(FeeResult {
2376 processing_fee: 999,
2377 ..Default::default()
2378 }),
2379 ];
2380 let combined = LowLevelDriveOperation::combine_cost_operations(&operations);
2381 assert_eq!(combined, OperationCost::default());
2382 }
2383
2384 fn make_grove_op(key_byte: u8) -> LowLevelDriveOperation {
2390 LowLevelDriveOperation::insert_for_known_path_key_element(
2391 vec![vec![0]],
2392 vec![key_byte],
2393 Element::new_item(vec![key_byte]),
2394 )
2395 }
2396
2397 fn make_mixed_ops() -> Vec<LowLevelDriveOperation> {
2398 vec![
2399 make_grove_op(1),
2400 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2401 make_grove_op(2),
2402 CalculatedCostOperation(OperationCost::default()),
2403 make_grove_op(3),
2404 ]
2405 }
2406
2407 #[test]
2408 fn grovedb_operations_batch_filters_grove_ops_from_ref() {
2409 let ops = make_mixed_ops();
2410 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2411 assert_eq!(batch.len(), 3);
2412 }
2413
2414 #[test]
2415 fn grovedb_operations_batch_empty_input() {
2416 let batch = LowLevelDriveOperation::grovedb_operations_batch(&[]);
2417 assert!(batch.is_empty());
2418 }
2419
2420 #[test]
2421 fn grovedb_operations_batch_no_grove_ops() {
2422 let ops = vec![
2423 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Blake3, 1)),
2424 CalculatedCostOperation(OperationCost::default()),
2425 ];
2426 let batch = LowLevelDriveOperation::grovedb_operations_batch(&ops);
2427 assert!(batch.is_empty());
2428 }
2429
2430 #[test]
2431 fn grovedb_operations_batch_consume_filters_grove_ops() {
2432 let ops = make_mixed_ops();
2433 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(ops);
2434 assert_eq!(batch.len(), 3);
2435 }
2436
2437 #[test]
2438 fn grovedb_operations_batch_consume_empty_input() {
2439 let batch = LowLevelDriveOperation::grovedb_operations_batch_consume(vec![]);
2440 assert!(batch.is_empty());
2441 }
2442
2443 #[test]
2444 fn grovedb_operations_batch_consume_with_leftovers_partitions_correctly() {
2445 let ops = make_mixed_ops();
2446 let (batch, leftovers) =
2447 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2448 assert_eq!(batch.len(), 3);
2449 assert_eq!(leftovers.len(), 2);
2450
2451 for leftover in &leftovers {
2453 assert!(
2454 !matches!(leftover, GroveOperation(_)),
2455 "leftovers should not contain GroveOperation variants"
2456 );
2457 }
2458 }
2459
2460 #[test]
2461 fn grovedb_operations_batch_consume_with_leftovers_all_grove() {
2462 let ops = vec![make_grove_op(10), make_grove_op(20)];
2463 let (batch, leftovers) =
2464 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2465 assert_eq!(batch.len(), 2);
2466 assert!(leftovers.is_empty());
2467 }
2468
2469 #[test]
2470 fn grovedb_operations_batch_consume_with_leftovers_no_grove() {
2471 let ops = vec![
2472 CalculatedCostOperation(OperationCost::default()),
2473 FunctionOperation(FunctionOp::new_with_round_count(HashFunction::Sha256, 1)),
2474 ];
2475 let (batch, leftovers) =
2476 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2477 assert!(batch.is_empty());
2478 assert_eq!(leftovers.len(), 2);
2479 }
2480
2481 #[test]
2482 fn grovedb_operations_batch_consume_with_leftovers_keeps_ephemeral_ops() {
2483 let ops = vec![
2484 make_grove_op(10),
2485 make_grove_op(20).retag_ephemeral(),
2486 CalculatedCostOperation(OperationCost::default()),
2487 ];
2488 let (batch, leftovers) =
2489 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(ops);
2490 assert_eq!(batch.len(), 1, "only the ordinary grove op joins the batch");
2491 assert_eq!(leftovers.len(), 2);
2492 assert!(
2493 leftovers
2494 .iter()
2495 .any(|op| matches!(op, EphemeralGroveOperation(..))),
2496 "an ephemeral grove op must survive as a leftover, never be dropped"
2497 );
2498 }
2499
2500 #[test]
2501 fn grovedb_operations_batch_consume_with_leftovers_empty() {
2502 let (batch, leftovers) =
2503 LowLevelDriveOperation::grovedb_operations_batch_consume_with_leftovers(vec![]);
2504 assert!(batch.is_empty());
2505 assert!(leftovers.is_empty());
2506 }
2507
2508 #[test]
2513 fn ephemeral_cost_zero_operation() {
2514 let fv = fee_version();
2515 let cost = OperationCost::default();
2516 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2517 assert_eq!(result, 0);
2518 }
2519
2520 #[test]
2521 fn ephemeral_cost_seek_only() {
2522 let fv = fee_version();
2523 let cost = OperationCost {
2524 seek_count: 5,
2525 storage_cost: StorageCost::default(),
2526 storage_loaded_bytes: 0,
2527 hash_node_calls: 0,
2528 sinsemilla_hash_calls: 0,
2529 };
2530 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2531 let expected = 5u64 * fv.storage.storage_seek_cost;
2532 assert_eq!(result, expected);
2533 }
2534
2535 #[test]
2536 fn ephemeral_cost_storage_added_bytes() {
2537 let fv = fee_version();
2538 let cost = OperationCost {
2539 seek_count: 0,
2540 storage_cost: StorageCost {
2541 added_bytes: 100,
2542 replaced_bytes: 0,
2543 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2544 },
2545 storage_loaded_bytes: 0,
2546 hash_node_calls: 0,
2547 sinsemilla_hash_calls: 0,
2548 };
2549 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2550 let expected = 100u64 * fv.storage.storage_processing_credit_per_byte;
2551 assert_eq!(result, expected);
2552 }
2553
2554 #[test]
2555 fn ephemeral_cost_storage_replaced_bytes() {
2556 let fv = fee_version();
2557 let cost = OperationCost {
2558 seek_count: 0,
2559 storage_cost: StorageCost {
2560 added_bytes: 0,
2561 replaced_bytes: 50,
2562 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2563 },
2564 storage_loaded_bytes: 0,
2565 hash_node_calls: 0,
2566 sinsemilla_hash_calls: 0,
2567 };
2568 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2569 let expected = 50u64 * fv.storage.storage_processing_credit_per_byte;
2570 assert_eq!(result, expected);
2571 }
2572
2573 #[test]
2574 fn ephemeral_cost_storage_removed_bytes_basic() {
2575 let fv = fee_version();
2576 let cost = OperationCost {
2577 seek_count: 0,
2578 storage_cost: StorageCost {
2579 added_bytes: 0,
2580 replaced_bytes: 0,
2581 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(75),
2582 },
2583 storage_loaded_bytes: 0,
2584 hash_node_calls: 0,
2585 sinsemilla_hash_calls: 0,
2586 };
2587 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2588 let expected = 75u64 * fv.storage.storage_processing_credit_per_byte;
2589 assert_eq!(result, expected);
2590 }
2591
2592 #[test]
2593 fn ephemeral_cost_loaded_bytes() {
2594 let fv = fee_version();
2595 let cost = OperationCost {
2596 seek_count: 0,
2597 storage_cost: StorageCost::default(),
2598 storage_loaded_bytes: 300,
2599 hash_node_calls: 0,
2600 sinsemilla_hash_calls: 0,
2601 };
2602 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2603 let expected = 300u64 * fv.storage.storage_load_credit_per_byte;
2604 assert_eq!(result, expected);
2605 }
2606
2607 #[test]
2608 fn ephemeral_cost_hash_node_calls() {
2609 let fv = fee_version();
2610 let cost = OperationCost {
2611 seek_count: 0,
2612 storage_cost: StorageCost::default(),
2613 storage_loaded_bytes: 0,
2614 hash_node_calls: 10,
2615 sinsemilla_hash_calls: 0,
2616 };
2617 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2618 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2619 let expected = blake3_total * 10;
2620 assert_eq!(result, expected);
2621 }
2622
2623 #[test]
2624 fn ephemeral_cost_sinsemilla_hash_calls() {
2625 let fv = fee_version();
2626 let cost = OperationCost {
2627 seek_count: 0,
2628 storage_cost: StorageCost::default(),
2629 storage_loaded_bytes: 0,
2630 hash_node_calls: 0,
2631 sinsemilla_hash_calls: 3,
2632 };
2633 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2634 let expected = fv.hashing.sinsemilla_base * 3;
2635 assert_eq!(result, expected);
2636 }
2637
2638 #[test]
2639 fn ephemeral_cost_all_components_combined() {
2640 let fv = fee_version();
2641 let cost = OperationCost {
2642 seek_count: 2,
2643 storage_cost: StorageCost {
2644 added_bytes: 10,
2645 replaced_bytes: 20,
2646 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(30),
2647 },
2648 storage_loaded_bytes: 40,
2649 hash_node_calls: 5,
2650 sinsemilla_hash_calls: 1,
2651 };
2652 let result = cost.ephemeral_cost(fv).expect("should not overflow");
2653
2654 let seek_cost = 2u64 * fv.storage.storage_seek_cost;
2655 let processing_per_byte = fv.storage.storage_processing_credit_per_byte;
2656 let added_cost = 10u64 * processing_per_byte;
2657 let replaced_cost = 20u64 * processing_per_byte;
2658 let removed_cost = 30u64 * processing_per_byte;
2659 let loaded_cost = 40u64 * fv.storage.storage_load_credit_per_byte;
2660 let blake3_total = fv.hashing.blake3_base + fv.hashing.blake3_per_block;
2661 let hash_cost = blake3_total * 5;
2662 let sinsemilla_cost = fv.hashing.sinsemilla_base * 1;
2663
2664 let expected = seek_cost
2665 + added_cost
2666 + replaced_cost
2667 + loaded_cost
2668 + removed_cost
2669 + hash_cost
2670 + sinsemilla_cost;
2671 assert_eq!(result, expected);
2672 }
2673
2674 #[test]
2675 fn ephemeral_cost_overflow_seek_cost() {
2676 let fv = &FeeVersion {
2677 storage: FeeStorageVersion {
2678 storage_seek_cost: u64::MAX,
2679 ..fee_version().storage.clone()
2680 },
2681 ..fee_version().clone()
2682 };
2683 let cost = OperationCost {
2684 seek_count: 2, storage_cost: StorageCost::default(),
2686 storage_loaded_bytes: 0,
2687 hash_node_calls: 0,
2688 sinsemilla_hash_calls: 0,
2689 };
2690 let result = cost.ephemeral_cost(fv);
2691 assert!(result.is_err(), "expected overflow error for seek cost");
2692 }
2693
2694 #[test]
2695 fn ephemeral_cost_overflow_storage_written_bytes() {
2696 let fv = &FeeVersion {
2697 storage: FeeStorageVersion {
2698 storage_processing_credit_per_byte: u64::MAX,
2699 ..fee_version().storage.clone()
2700 },
2701 ..fee_version().clone()
2702 };
2703 let cost = OperationCost {
2704 seek_count: 0,
2705 storage_cost: StorageCost {
2706 added_bytes: 2, replaced_bytes: 0,
2708 removed_bytes: StorageRemovedBytes::NoStorageRemoval,
2709 },
2710 storage_loaded_bytes: 0,
2711 hash_node_calls: 0,
2712 sinsemilla_hash_calls: 0,
2713 };
2714 let result = cost.ephemeral_cost(fv);
2715 assert!(
2716 result.is_err(),
2717 "expected overflow error for storage written bytes"
2718 );
2719 }
2720
2721 #[test]
2722 fn ephemeral_cost_overflow_loaded_bytes() {
2723 let fv = &FeeVersion {
2724 storage: FeeStorageVersion {
2725 storage_load_credit_per_byte: u64::MAX,
2726 ..fee_version().storage.clone()
2727 },
2728 ..fee_version().clone()
2729 };
2730 let cost = OperationCost {
2731 seek_count: 0,
2732 storage_cost: StorageCost::default(),
2733 storage_loaded_bytes: 2, hash_node_calls: 0,
2735 sinsemilla_hash_calls: 0,
2736 };
2737 let result = cost.ephemeral_cost(fv);
2738 assert!(
2739 result.is_err(),
2740 "expected overflow error for loaded bytes cost"
2741 );
2742 }
2743
2744 #[test]
2750 fn empty_tree_operation_for_known_path_key_provable_sum_tree() {
2751 use grovedb::batch::GroveOp;
2752
2753 let op = TreeType::ProvableSumTree
2754 .empty_tree_operation_for_known_path_key(vec![b"root".to_vec()], b"k".to_vec(), None)
2755 .expect("empty_tree_operation_for_known_path_key");
2756
2757 match op {
2758 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2759 GroveOp::InsertOrReplace { element }
2760 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element } => assert!(
2761 matches!(element, Element::ProvableSumTree(..)),
2762 "expected ProvableSumTree element, got: {:?}",
2763 element
2764 ),
2765 other => panic!("expected GroveOp::InsertOrReplace, got: {:?}", other),
2766 },
2767 other => panic!("expected GroveOperation, got: {:?}", other),
2768 }
2769 }
2770
2771 #[test]
2779 fn zero_contribution_dispatcher_full_matrix() {
2780 use grovedb::batch::GroveOp;
2781
2782 const ALL_INNERS: [TreeType; 9] = [
2783 TreeType::NormalTree,
2784 TreeType::SumTree,
2785 TreeType::BigSumTree,
2786 TreeType::CountTree,
2787 TreeType::CountSumTree,
2788 TreeType::ProvableCountTree,
2789 TreeType::ProvableCountSumTree,
2790 TreeType::ProvableSumTree,
2791 TreeType::ProvableCountProvableSumTree,
2792 ];
2793
2794 fn is_sum_bearing(tree_type: TreeType) -> bool {
2795 matches!(
2796 tree_type,
2797 TreeType::SumTree
2798 | TreeType::BigSumTree
2799 | TreeType::CountSumTree
2800 | TreeType::ProvableCountSumTree
2801 | TreeType::ProvableSumTree
2802 | TreeType::ProvableCountProvableSumTree
2803 )
2804 }
2805
2806 fn element_tree_type(element: &Element) -> TreeType {
2807 match element {
2808 Element::Tree(..) => TreeType::NormalTree,
2809 Element::SumTree(..) => TreeType::SumTree,
2810 Element::BigSumTree(..) => TreeType::BigSumTree,
2811 Element::CountTree(..) => TreeType::CountTree,
2812 Element::CountSumTree(..) => TreeType::CountSumTree,
2813 Element::ProvableCountTree(..) => TreeType::ProvableCountTree,
2814 Element::ProvableCountSumTree(..) => TreeType::ProvableCountSumTree,
2815 Element::ProvableSumTree(..) => TreeType::ProvableSumTree,
2816 Element::ProvableCountProvableSumTree(..) => TreeType::ProvableCountProvableSumTree,
2817 other => panic!("unexpected inner element: {other:?}"),
2818 }
2819 }
2820
2821 #[derive(Debug, PartialEq)]
2822 enum Expected {
2823 NonCounted,
2824 NotSummed,
2825 NotCountedOrSummed,
2826 Unwrapped,
2827 }
2828
2829 let dispatch = |parent: TreeType, inner: TreeType| {
2830 LowLevelDriveOperation::for_known_path_key_empty_tree_contributing_zero_to_parent(
2831 vec![b"root".to_vec()],
2832 b"key".to_vec(),
2833 parent,
2834 inner,
2835 None,
2836 )
2837 };
2838
2839 let assert_cell = |parent: TreeType, inner: TreeType, expected: Expected| {
2840 let op = dispatch(parent, inner).unwrap_or_else(|error| {
2841 panic!("parent {parent:?} inner {inner:?} must be accepted: {error}")
2842 });
2843 let element = match op {
2844 LowLevelDriveOperation::GroveOperation(grove_op) => match grove_op.op {
2845 GroveOp::InsertOrReplace { element }
2846 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element } => {
2847 element
2848 }
2849 other => panic!("expected InsertOrReplace, got {other:?}"),
2850 },
2851 other => panic!("expected GroveOperation, got {other:?}"),
2852 };
2853 let (wrapper, produced_inner) = match &element {
2854 Element::NonCounted(inner_element) => {
2855 (Expected::NonCounted, inner_element.as_ref())
2856 }
2857 Element::NotSummed(inner_element) => (Expected::NotSummed, inner_element.as_ref()),
2858 Element::NotCountedOrSummed(inner_element) => {
2859 (Expected::NotCountedOrSummed, inner_element.as_ref())
2860 }
2861 plain => (Expected::Unwrapped, plain),
2862 };
2863 assert_eq!(
2864 wrapper, expected,
2865 "parent {parent:?} inner {inner:?}: wrong wrapper"
2866 );
2867 assert_eq!(
2868 element_tree_type(produced_inner),
2869 inner,
2870 "parent {parent:?} inner {inner:?}: wrong inner tree type"
2871 );
2872 };
2873
2874 for inner in ALL_INNERS {
2876 assert_cell(TreeType::CountTree, inner, Expected::NonCounted);
2877 }
2878 for inner in ALL_INNERS {
2881 let expected = if is_sum_bearing(inner) {
2882 Expected::NotCountedOrSummed
2883 } else {
2884 Expected::NonCounted
2885 };
2886 assert_cell(TreeType::CountSumTree, inner, expected);
2887 }
2888 for parent in [
2891 TreeType::SumTree,
2892 TreeType::BigSumTree,
2893 TreeType::ProvableSumTree,
2894 ] {
2895 for inner in ALL_INNERS {
2896 let expected = if is_sum_bearing(inner) {
2897 Expected::NotSummed
2898 } else {
2899 Expected::Unwrapped
2900 };
2901 assert_cell(parent, inner, expected);
2902 }
2903 }
2904 for parent in [
2909 TreeType::NormalTree,
2910 TreeType::ProvableCountTree,
2911 TreeType::ProvableCountSumTree,
2912 TreeType::ProvableCountProvableSumTree,
2913 ] {
2914 for inner in ALL_INNERS {
2915 assert!(
2916 dispatch(parent, inner).is_err(),
2917 "parent {parent:?} inner {inner:?} must be rejected"
2918 );
2919 }
2920 }
2921
2922 const INDEXED: [TreeType; 3] = [
2928 TreeType::ProvableCountIndexedTree,
2929 TreeType::ProvableSumIndexedTree,
2930 TreeType::ProvableCountProvableSumIndexedTree,
2931 ];
2932 for indexed in INDEXED {
2933 for inner in ALL_INNERS {
2934 assert!(
2935 dispatch(indexed, inner).is_err(),
2936 "indexed parent {indexed:?} inner {inner:?} must be rejected"
2937 );
2938 }
2939 for parent in [
2940 TreeType::CountTree,
2941 TreeType::CountSumTree,
2942 TreeType::SumTree,
2943 TreeType::BigSumTree,
2944 TreeType::ProvableSumTree,
2945 ] {
2946 assert!(
2947 dispatch(parent, indexed).is_err(),
2948 "parent {parent:?} indexed inner {indexed:?} must be rejected"
2949 );
2950 }
2951 }
2952 }
2953
2954 #[test]
2955 fn ephemeral_cost_overflow_in_addition_chain() {
2956 let fv = fee_version();
2958 let cost = OperationCost {
2959 seek_count: u32::MAX,
2960 storage_cost: StorageCost {
2961 added_bytes: u32::MAX,
2962 replaced_bytes: u32::MAX,
2963 removed_bytes: StorageRemovedBytes::BasicStorageRemoval(u32::MAX),
2964 },
2965 storage_loaded_bytes: u64::MAX,
2966 hash_node_calls: u32::MAX,
2967 sinsemilla_hash_calls: u32::MAX,
2968 };
2969 let result = cost.ephemeral_cost(fv);
2970 assert!(
2971 result.is_err(),
2972 "expected overflow error when summing large components"
2973 );
2974 }
2975}