Skip to content

Commit

Permalink
feat: PathQuery depth (#328)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuantumExplorer authored Aug 20, 2024
1 parent a054032 commit 7c37bb7
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 1 deletion.
5 changes: 4 additions & 1 deletion grovedb/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ fn main() {
let grovedbg_zip_path = out_dir.join("grovedbg.zip");

if !grovedbg_zip_path.exists() {
let response = reqwest::blocking::get(format!("https://github.com/dashpay/grovedbg/releases/download/{GROVEDBG_VERSION}/grovedbg-{GROVEDBG_VERSION}.zip"))
let response = reqwest::blocking::get(format!(
"https://github.com/dashpay/grovedbg/releases/download/{GROVEDBG_VERSION}/\
grovedbg-{GROVEDBG_VERSION}.zip"
))
.expect("can't download GroveDBG artifact");

let mut grovedbg_zip = File::create(&grovedbg_zip_path).unwrap();
Expand Down
43 changes: 43 additions & 0 deletions grovedb/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ impl PathQuery {
Self { path, query }
}

/// The max depth of the query, this is the maximum layers we could get back
/// from grovedb
/// If the max depth can not be calculated we get None
/// This would occur if the recursion level was too high
pub fn max_depth(&self) -> Option<u16> {
self.query.query.max_depth()
}

/// Gets the path of all terminal keys
pub fn terminal_keys(
&self,
Expand Down Expand Up @@ -1634,6 +1642,8 @@ mod tests {
},
};

assert_eq!(path_query.max_depth(), Some(4));

{
let path = vec![];
let first = path_query
Expand Down Expand Up @@ -1710,4 +1720,37 @@ mod tests {
);
}
}

#[test]
fn test_max_depth_limit() {
/// Creates a `Query` with nested `SubqueryBranch` up to the specified
/// depth non-recursively.
fn create_non_recursive_query(subquery_depth: usize) -> Query {
let mut root_query = Query::new_range_full();
let mut current_query = &mut root_query;

for _ in 0..subquery_depth {
let new_query = Query::new_range_full();
current_query.default_subquery_branch = SubqueryBranch {
subquery_path: None,
subquery: Some(Box::new(new_query)),
};
current_query = current_query
.default_subquery_branch
.subquery
.as_mut()
.unwrap();
}

root_query
}

let query = create_non_recursive_query(100);

assert_eq!(query.max_depth(), Some(101));

let query = create_non_recursive_query(500);

assert_eq!(query.max_depth(), None);
}
}
57 changes: 57 additions & 0 deletions merk/src/proofs/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,36 @@ pub struct SubqueryBranch {
pub subquery: Option<Box<Query>>,
}

impl SubqueryBranch {
/// Returns the depth of the subquery branch
/// This depth is how many GroveDB layers down we could query at maximum
#[inline]
pub fn max_depth(&self) -> Option<u16> {
self.max_depth_internal(u8::MAX)
}

/// Returns the depth of the subquery branch
/// This depth is how many GroveDB layers down we could query at maximum
#[inline]
fn max_depth_internal(&self, recursion_limit: u8) -> Option<u16> {
if recursion_limit == 0 {
return None;
}
let subquery_path_depth = self.subquery_path.as_ref().map_or(Some(0), |path| {
let path_len = path.len();
if path_len > u16::MAX as usize {
None
} else {
Some(path_len as u16)
}
})?;
let subquery_depth = self.subquery.as_ref().map_or(Some(0), |query| {
query.max_depth_internal(recursion_limit - 1)
})?;
subquery_path_depth.checked_add(subquery_depth)
}
}

#[cfg(any(feature = "full", feature = "verify"))]
/// `Query` represents one or more keys or ranges of keys, which can be used to
/// resolve a proof which will include all the requested values.
Expand Down Expand Up @@ -476,6 +506,33 @@ impl Query {
// checks if all searched for items are keys
self.items.iter().all(|a| a.is_key())
}

/// Returns the depth of the subquery branch
/// This depth is how many GroveDB layers down we could query at maximum
pub fn max_depth(&self) -> Option<u16> {
self.max_depth_internal(u8::MAX)
}

/// Returns the depth of the subquery branch
/// This depth is how many GroveDB layers down we could query at maximum
pub(crate) fn max_depth_internal(&self, recursion_limit: u8) -> Option<u16> {
let default_subquery_branch_depth = self
.default_subquery_branch
.max_depth_internal(recursion_limit)?;
let conditional_subquery_branches_max_depth = self
.conditional_subquery_branches
.as_ref()
.map_or(Some(0), |condition_subqueries| {
condition_subqueries
.values()
.try_fold(0, |max_depth, conditional_subquery_branch| {
conditional_subquery_branch
.max_depth_internal(recursion_limit)
.map(|depth| max_depth.max(depth))
})
})?;
1u16.checked_add(default_subquery_branch_depth.max(conditional_subquery_branches_max_depth))
}
}

#[cfg(feature = "full")]
Expand Down

0 comments on commit 7c37bb7

Please sign in to comment.