Skip to main content

drive/util/grove_operations/
mod.rs

1//! Grove Operations.
2//!
3//! Defines and implements in Drive functions pertinent to groveDB operations.
4//!
5
6/// Grove insert operation
7pub mod grove_insert;
8
9/// Grove insert operation into an empty tree
10pub mod grove_insert_empty_tree;
11
12/// Grove insert operation, but only if it doesn't already exist
13pub mod grove_insert_if_not_exists;
14
15/// Grove delete operation
16pub mod grove_delete;
17
18/// Fetch raw grove data
19pub mod grove_get_raw;
20
21/// Fetch raw grove data and match that is item
22pub mod grove_get_raw_item;
23
24/// Fetch raw grove data if it exists
25pub mod grove_get_raw_optional;
26
27/// Fetch u64 value from encoded variable vector in raw grove data
28pub mod grove_get_raw_value_u64_from_encoded_var_vec;
29
30/// Grove get operation
31pub mod grove_get;
32
33/// Serialized results from grove path query
34pub mod grove_get_path_query_serialized_results;
35
36/// Grove path query operation
37pub mod grove_get_path_query;
38
39/// Grove path query operation with optional return value
40pub mod grove_get_path_query_with_optional;
41
42/// Fetch raw data from grove path query with optional return value
43pub mod grove_get_raw_path_query_with_optional;
44
45/// Fetch raw data from grove path query
46pub mod grove_get_raw_path_query;
47
48/// Proved path query in grove
49pub mod grove_get_proved_path_query;
50
51/// V1 proved path query in grove (supports BulkAppendTree/CommitmentTree)
52pub mod grove_get_proved_path_query_v1;
53
54/// Get total count from a CommitmentTree
55pub mod grove_commitment_tree_count;
56
57/// Proved branch chunk query in grove
58pub mod grove_get_proved_branch_chunk_query;
59
60/// Proved trunk chunk query in grove
61pub mod grove_get_proved_trunk_chunk_query;
62
63/// Get total value from sum tree in grove
64pub mod grove_get_sum_tree_total_value;
65
66/// Check if raw data exists in grove
67pub mod grove_has_raw;
68
69/// Batch insert operation into empty tree
70pub mod batch_insert_empty_tree;
71
72/// Batch insert operation into empty sum tree
73pub mod batch_insert_empty_sum_tree;
74
75/// Batch insert operation into empty count tree (O(1) total count)
76pub mod batch_insert_empty_count_tree;
77
78/// Batch insert operation into empty count-sum tree (O(1) totals for both
79/// count and sum, no per-node aggregation). Used when a document type
80/// opts into BOTH `documentsCountable` and `documentsSummable` without
81/// any range-* flags.
82pub mod batch_insert_empty_count_sum_tree;
83
84/// Batch insert operation into empty provable count tree (range-countable)
85pub mod batch_insert_empty_provable_count_tree;
86
87/// Batch insert operation into empty provable sum tree (range-summable).
88/// Mirrors [`batch_insert_empty_provable_count_tree`] for the sum surface
89/// — commits per-node aggregated sums to every internal merk node so
90/// range queries land on an O(log n) `AggregateSumOnRange` proof.
91pub mod batch_insert_empty_provable_sum_tree;
92
93/// Batch insert operation into empty provable count-sum tree (combined
94/// count+sum surface). Used when an index opts into both `rangeCountable`
95/// and `rangeSummable` — a single tree carries both metrics per-node.
96/// Lights up once grovedb PR 670 ships `Element::ProvableCountSumTree`
97/// as a callable element variant.
98pub mod batch_insert_empty_provable_count_sum_tree;
99
100/// Batch insert operation into empty provable-count + provable-sum tree
101/// (PCPS, the fully-provable combined surface). Used when an index opts
102/// into BOTH `rangeCountable: true` AND `rangeSummable: true` —
103/// per-node counts AND per-node sums are committed to every internal
104/// merk node so range queries can answer
105/// `AggregateCountOnRange`/`AggregateSumOnRange` (and the combined
106/// variant once grovedb PR 670 ships) over the same tree.
107pub mod batch_insert_empty_provable_count_provable_sum_tree;
108
109/// Batch insert operation into empty tree, but only if it doesn't already exist
110pub mod batch_insert_empty_tree_if_not_exists;
111
112/// Batch insert operation into empty tree, but only if it doesn't exist and check existing operations
113pub mod batch_insert_empty_tree_if_not_exists_check_existing_operations;
114
115/// Batch insert operation
116pub mod batch_insert;
117
118/// Batch replace operation
119pub mod batch_replace;
120
121/// Batch insert operation, but only if it doesn't already exist
122pub mod batch_insert_if_not_exists;
123
124/// Batch insert operation, but only if the value has changed
125pub mod batch_insert_if_changed_value;
126
127/// Batch delete operation
128pub mod batch_delete;
129
130/// Batch remove raw data operation
131pub mod batch_remove_raw;
132
133/// Batch delete operation up the tree while it's empty
134pub mod batch_delete_up_tree_while_empty;
135
136/// Batch refresh reference operation
137pub mod batch_refresh_reference;
138
139/// Apply grove operation
140pub mod grove_apply_operation;
141
142/// Apply batch grove operation
143pub mod grove_apply_batch;
144
145/// Apply batch grove operation with additional costs
146pub mod grove_apply_batch_with_add_costs;
147
148/// Apply partial batch grove operation
149pub mod grove_apply_partial_batch;
150
151/// Apply partial batch grove operation with additional costs
152pub mod grove_apply_partial_batch_with_add_costs;
153
154/// Get cost of grove batch operations
155pub mod grove_batch_operations_costs;
156
157/// Clear a subtree in grovedb
158pub mod grove_clear;
159
160/// Provides functionality to delete items in a path based on a query.
161pub mod batch_delete_items_in_path_query;
162
163/// Inserts an element if it does not exist and returns the existing element if it does.
164pub mod batch_insert_if_not_exists_return_existing_element;
165
166/// Inserts a sum item or adds to it if it already exists.
167pub mod batch_insert_sum_item_or_add_to_if_already_exists;
168
169/// Retrieves serialized or sum results from a path query in GroveDB.
170mod grove_get_path_query_serialized_or_sum_results;
171
172/// Executes a proved path query in GroveDB with an optional conditional query.
173pub mod grove_get_proved_path_query_with_conditional;
174
175/// Inserts an element if it does not exist and returns the existing element if it does in GroveDB.
176pub mod grove_insert_if_not_exists_return_existing_element;
177
178/// Batch inserts sum item if not already existing
179pub mod batch_insert_sum_item_if_not_exists;
180/// Moved items that are found in a path query to a new path.
181pub mod batch_move_items_in_path_query;
182
183/// Batch inserts item with sum item if not already existing
184pub mod batch_insert_item_with_sum_item_if_not_exists;
185/// Keeps the item, but inserts or adds to the sum item if it already exists
186pub mod batch_keep_item_insert_sum_item_or_add_to_if_already_exists;
187mod batch_move;
188/// Get the total value from a big sum tree
189pub mod grove_get_big_sum_tree_total_value;
190/// Get total value from sum tree in grove if it exists
191pub mod grove_get_optional_sum_tree_total_value;
192/// Fetch raw grove data if it exists, None otherwise
193pub mod grove_get_raw_optional_item;
194
195use grovedb_costs::CostContext;
196
197use grovedb::{EstimatedLayerInformation, MaybeTree, TreeType};
198
199use crate::error::Error;
200use crate::fees::op::LowLevelDriveOperation;
201use crate::fees::op::LowLevelDriveOperation::CalculatedCostOperation;
202
203use grovedb::Error as GroveError;
204
205use intmap::IntMap;
206
207/// Pushes an operation's `OperationCost` to `drive_operations` given its `CostContext`
208/// and returns the operation's return value.
209fn push_drive_operation_result<T>(
210    cost_context: CostContext<Result<T, GroveError>>,
211    drive_operations: &mut Vec<LowLevelDriveOperation>,
212) -> Result<T, Error> {
213    let CostContext { value, cost } = cost_context;
214    if !cost.is_nothing() {
215        drive_operations.push(CalculatedCostOperation(cost));
216    }
217    value.map_err(Error::from)
218}
219
220/// Pushes an operation's `OperationCost` to `drive_operations` given its `CostContext`
221/// if `drive_operations` is given. Returns the operation's return value.
222fn push_drive_operation_result_optional<T>(
223    cost_context: CostContext<Result<T, GroveError>>,
224    drive_operations: Option<&mut Vec<LowLevelDriveOperation>>,
225) -> Result<T, Error> {
226    let CostContext { value, cost } = cost_context;
227    if let Some(drive_operations) = drive_operations {
228        drive_operations.push(CalculatedCostOperation(cost));
229    }
230    value.map_err(Error::from)
231}
232/// Is subtree?
233pub type IsSubTree = bool;
234/// Is sum subtree?
235pub type IsSumSubTree = bool;
236/// Is sum tree?
237pub type IsSumTree = bool;
238
239/// Batch delete apply type
240#[derive(Debug, Copy, Clone)]
241pub enum BatchDeleteApplyType {
242    /// Stateless batch delete
243    StatelessBatchDelete {
244        /// Are we deleting in a sum tree
245        in_tree_type: TreeType,
246        /// What is the estimated key size
247        estimated_key_size: u32,
248        /// What is the estimated value size
249        estimated_value_size: u32,
250    },
251    /// Stateful batch delete
252    StatefulBatchDelete {
253        /// Are we known to be in a subtree and does this subtree have sums
254        is_known_to_be_subtree_with_sum: Option<MaybeTree>,
255    },
256}
257
258/// Batch move apply type
259#[derive(Debug, Copy, Clone)]
260pub enum BatchMoveApplyType {
261    /// Stateless batch move
262    StatelessBatchMove {
263        /// What type of tree are we in for the move
264        in_tree_type: TreeType,
265        /// Are we moving a trees?
266        tree_type: Option<TreeType>,
267        /// What is the estimated key size
268        estimated_key_size: u32,
269        /// What is the estimated value size
270        estimated_value_size: u32,
271        /// The flags length
272        flags_len: FlagsLen,
273    },
274    /// Stateful batch move
275    StatefulBatchMove {
276        /// Are we known to be in a subtree and does this subtree have sums
277        is_known_to_be_subtree_with_sum: Option<MaybeTree>,
278    },
279}
280
281#[derive(Clone)]
282/// Batch delete up tree apply type
283pub enum BatchDeleteUpTreeApplyType {
284    /// Stateless batch delete
285    StatelessBatchDelete {
286        /// The estimated layer info
287        estimated_layer_info: IntMap<u16, EstimatedLayerInformation>,
288    },
289    /// Stateful batch delete
290    StatefulBatchDelete {
291        /// Are we known to be in a subtree and does this subtree have sums
292        is_known_to_be_subtree_with_sum: Option<MaybeTree>,
293    },
294}
295
296/// batch insert tree apply type
297#[derive(Clone, Copy)]
298/// Batch insert tree apply type
299pub enum BatchInsertTreeApplyType {
300    /// Stateless batch insert tree
301    StatelessBatchInsertTree {
302        /// Does this tree use sums?
303        in_tree_type: TreeType,
304        /// Are we inserting in a sum tree
305        tree_type: TreeType,
306        /// The flags length
307        flags_len: FlagsLen,
308    },
309    /// Stateful batch insert tree
310    StatefulBatchInsertTree,
311}
312
313/// Represents the types for batch insert operations in a tree structure.
314impl BatchInsertTreeApplyType {
315    /// Converts the current `BatchInsertTreeApplyType` into a corresponding `DirectQueryType`.
316    ///
317    /// # Returns
318    ///
319    /// - A variant of `DirectQueryType::StatelessDirectQuery` if the current type is `BatchInsertTreeApplyType::StatelessBatchInsertTree`.
320    /// - `DirectQueryType::StatefulDirectQuery` if the current type is `BatchInsertTreeApplyType::StatefulBatchInsertTree`.
321    /// ```
322    pub(crate) fn to_direct_query_type(self) -> DirectQueryType {
323        match self {
324            BatchInsertTreeApplyType::StatelessBatchInsertTree {
325                in_tree_type,
326                tree_type,
327                flags_len,
328            } => DirectQueryType::StatelessDirectQuery {
329                in_tree_type,
330                query_target: QueryTarget::QueryTargetTree(flags_len, tree_type),
331            },
332            BatchInsertTreeApplyType::StatefulBatchInsertTree => {
333                DirectQueryType::StatefulDirectQuery
334            }
335        }
336    }
337}
338
339/// Batch insert apply type
340#[derive(Clone, Copy)]
341pub enum BatchInsertApplyType {
342    /// Stateless batch insert
343    StatelessBatchInsert {
344        /// Does this tree use sums?
345        in_tree_type: TreeType,
346        /// the type of Target (Tree or Value)
347        target: QueryTarget,
348    },
349    /// Stateful batch insert
350    StatefulBatchInsert,
351}
352
353impl BatchInsertApplyType {
354    /// Converts the current `BatchInsertApplyType` into a corresponding `DirectQueryType`.
355    ///
356    /// # Returns
357    ///
358    /// - A variant of `DirectQueryType::StatelessDirectQuery` if the current type is `BatchInsertApplyType::StatelessBatchInsert`.
359    /// - `DirectQueryType::StatefulDirectQuery` if the current type is `BatchInsertApplyType::StatefulBatchInsert`.
360    /// ```
361    // TODO: Not using
362    #[allow(dead_code)]
363    #[allow(clippy::wrong_self_convention)]
364    pub(crate) fn to_direct_query_type(&self) -> DirectQueryType {
365        match self {
366            BatchInsertApplyType::StatelessBatchInsert {
367                in_tree_type: in_tree_using_sums,
368                target,
369            } => DirectQueryType::StatelessDirectQuery {
370                in_tree_type: *in_tree_using_sums,
371                query_target: *target,
372            },
373            BatchInsertApplyType::StatefulBatchInsert => DirectQueryType::StatefulDirectQuery,
374        }
375    }
376}
377
378/// Flags length
379pub type FlagsLen = u32;
380
381/// query target
382#[derive(Clone, Copy)]
383/// Query target
384pub enum QueryTarget {
385    /// tree
386    QueryTargetTree(FlagsLen, TreeType),
387    /// value
388    QueryTargetValue(u32),
389}
390
391impl QueryTarget {
392    /// Length
393    pub(crate) fn len(&self) -> u32 {
394        match self {
395            QueryTarget::QueryTargetTree(flags_len, tree_type) => {
396                *flags_len + tree_type.inner_node_type().cost() + 3
397            }
398            QueryTarget::QueryTargetValue(len) => *len,
399        }
400    }
401}
402
403/// direct query type
404#[derive(Clone, Copy)]
405/// Direct query type
406pub enum DirectQueryType {
407    /// Stateless direct query
408    StatelessDirectQuery {
409        /// Does this tree use sums?
410        in_tree_type: TreeType,
411        /// the type of Target (Tree or Value)
412        query_target: QueryTarget,
413    },
414    /// Stateful direct query
415    StatefulDirectQuery,
416}
417
418impl From<DirectQueryType> for QueryType {
419    fn from(value: DirectQueryType) -> Self {
420        match value {
421            DirectQueryType::StatelessDirectQuery {
422                in_tree_type,
423                query_target,
424            } => QueryType::StatelessQuery {
425                in_tree_type,
426                query_target,
427                estimated_reference_sizes: vec![],
428            },
429            DirectQueryType::StatefulDirectQuery => QueryType::StatefulQuery,
430        }
431    }
432}
433
434impl DirectQueryType {
435    /// Converts the current `DirectQueryType` into a corresponding `QueryType`
436    /// while associating it with the given reference sizes.
437    ///
438    /// # Parameters
439    ///
440    /// * `reference_sizes`: A vector of `u32` values representing the reference sizes
441    ///   associated with the query.
442    ///
443    /// # Returns
444    ///
445    /// - A variant of `QueryType::StatelessQuery` with the provided reference sizes if
446    ///   the current type is `DirectQueryType::StatelessDirectQuery`.
447    /// - `QueryType::StatefulQuery` if the current type is `DirectQueryType::StatefulDirectQuery`.
448    ///
449    /// # Example
450    ///
451    /// ```ignore
452    /// let direct_query = DirectQueryType::StatelessDirectQuery {
453    ///     in_tree_using_sums: true,
454    ///     query_target: SomeTarget, // Replace with an actual target instance.
455    /// };
456    ///
457    /// let ref_sizes = vec![100, 200, 300];
458    /// let query_type = direct_query.add_reference_sizes(ref_sizes);
459    /// ```
460    #[allow(dead_code)]
461    #[deprecated(note = "This function is marked as unused.")]
462    #[allow(deprecated)]
463    pub(crate) fn add_reference_sizes(self, reference_sizes: Vec<u32>) -> QueryType {
464        match self {
465            DirectQueryType::StatelessDirectQuery {
466                in_tree_type: in_tree_using_sums,
467                query_target,
468            } => QueryType::StatelessQuery {
469                in_tree_type: in_tree_using_sums,
470                query_target,
471                estimated_reference_sizes: reference_sizes,
472            },
473            DirectQueryType::StatefulDirectQuery => QueryType::StatefulQuery,
474        }
475    }
476}
477
478/// Query type
479#[derive(Clone)]
480pub enum QueryType {
481    /// Stateless query
482    StatelessQuery {
483        /// Does this tree use sums?
484        in_tree_type: TreeType,
485        /// the type of Target (Tree or Value)
486        query_target: QueryTarget,
487        /// The estimated sizes of references
488        estimated_reference_sizes: Vec<u32>,
489    },
490    /// Stateful query
491    StatefulQuery,
492}
493
494impl From<BatchDeleteApplyType> for QueryType {
495    fn from(value: BatchDeleteApplyType) -> Self {
496        match value {
497            BatchDeleteApplyType::StatelessBatchDelete {
498                in_tree_type: is_sum_tree,
499                estimated_value_size,
500                ..
501            } => QueryType::StatelessQuery {
502                in_tree_type: is_sum_tree,
503                query_target: QueryTarget::QueryTargetValue(estimated_value_size),
504                estimated_reference_sizes: vec![],
505            },
506            BatchDeleteApplyType::StatefulBatchDelete { .. } => QueryType::StatefulQuery,
507        }
508    }
509}
510
511impl From<&BatchDeleteApplyType> for QueryType {
512    fn from(value: &BatchDeleteApplyType) -> Self {
513        match value {
514            BatchDeleteApplyType::StatelessBatchDelete {
515                in_tree_type: is_sum_tree,
516                estimated_value_size,
517                ..
518            } => QueryType::StatelessQuery {
519                in_tree_type: *is_sum_tree,
520                query_target: QueryTarget::QueryTargetValue(*estimated_value_size),
521                estimated_reference_sizes: vec![],
522            },
523            BatchDeleteApplyType::StatefulBatchDelete { .. } => QueryType::StatefulQuery,
524        }
525    }
526}
527
528impl From<BatchDeleteApplyType> for DirectQueryType {
529    fn from(value: BatchDeleteApplyType) -> Self {
530        match value {
531            BatchDeleteApplyType::StatelessBatchDelete {
532                in_tree_type: is_sum_tree,
533                estimated_value_size,
534                ..
535            } => DirectQueryType::StatelessDirectQuery {
536                in_tree_type: is_sum_tree,
537                query_target: QueryTarget::QueryTargetValue(estimated_value_size),
538            },
539            BatchDeleteApplyType::StatefulBatchDelete { .. } => {
540                DirectQueryType::StatefulDirectQuery
541            }
542        }
543    }
544}
545
546impl From<&BatchDeleteApplyType> for DirectQueryType {
547    fn from(value: &BatchDeleteApplyType) -> Self {
548        match value {
549            BatchDeleteApplyType::StatelessBatchDelete {
550                in_tree_type: is_sum_tree,
551                estimated_value_size,
552                ..
553            } => DirectQueryType::StatelessDirectQuery {
554                in_tree_type: *is_sum_tree,
555                query_target: QueryTarget::QueryTargetValue(*estimated_value_size),
556            },
557            BatchDeleteApplyType::StatefulBatchDelete { .. } => {
558                DirectQueryType::StatefulDirectQuery
559            }
560        }
561    }
562}
563
564/// Specifies which GroveDB instance to use for a query
565#[derive(Debug, Clone, Copy, PartialEq, Eq)]
566pub enum GroveDBToUse {
567    /// Use the current (main) GroveDB
568    Current,
569    /// Use the latest checkpoint
570    LatestCheckpoint,
571    /// Use a specific checkpoint at the given block height
572    Checkpoint(u64),
573}