From 5de7dc297df36d1caa2c80bad1a571ce5775b9fe Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 20 Aug 2024 10:36:22 +0700 Subject: [PATCH 1/5] feat: add max depth to pathqueries --- grovedb/build.rs | 5 ++++- grovedb/src/query/mod.rs | 8 ++++++++ merk/src/proofs/query/mod.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/grovedb/build.rs b/grovedb/build.rs index 83a38900..ae9e61c1 100644 --- a/grovedb/build.rs +++ b/grovedb/build.rs @@ -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(); diff --git a/grovedb/src/query/mod.rs b/grovedb/src/query/mod.rs index f140bb05..7bc41227 100644 --- a/grovedb/src/query/mod.rs +++ b/grovedb/src/query/mod.rs @@ -134,6 +134,12 @@ impl PathQuery { Self { path, query } } + /// The max depth od the query, this is the maximum layers we could get back + /// from grovedb + pub fn max_depth(&self) -> u32 { + self.query.query.max_depth() + } + /// Gets the path of all terminal keys pub fn terminal_keys( &self, @@ -1634,6 +1640,8 @@ mod tests { }, }; + assert_eq!(path_query.max_depth(), 4); + { let path = vec![]; let first = path_query diff --git a/merk/src/proofs/query/mod.rs b/merk/src/proofs/query/mod.rs index 669940cc..4cf25273 100644 --- a/merk/src/proofs/query/mod.rs +++ b/merk/src/proofs/query/mod.rs @@ -70,6 +70,20 @@ pub struct SubqueryBranch { pub subquery: Option>, } +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) -> u32 { + let subquery_path_depth = self + .subquery_path + .as_ref() + .map_or(0, |path| path.len() as u32); + let subquery_depth = self.subquery.as_ref().map_or(0, |query| query.max_depth()); + subquery_path_depth + 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. @@ -476,6 +490,23 @@ 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) -> u32 { + let default_subquery_branch_depth = self.default_subquery_branch.max_depth(); + let conditional_subquery_branches_max_depth = self + .conditional_subquery_branches + .as_ref() + .map_or(0, |condition_subqueries| { + condition_subqueries + .values() + .map(|conditional_subquery_branch| conditional_subquery_branch.max_depth()) + .max() + .unwrap_or_default() + }); + 1 + default_subquery_branch_depth.max(conditional_subquery_branches_max_depth) + } } #[cfg(feature = "full")] From a5302fcc06f5c13f5e2e8d027b6a6ab816a0c168 Mon Sep 17 00:00:00 2001 From: QuantumExplorer Date: Tue, 20 Aug 2024 15:18:17 +0700 Subject: [PATCH 2/5] Update grovedb/src/query/mod.rs Co-authored-by: fominok --- grovedb/src/query/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grovedb/src/query/mod.rs b/grovedb/src/query/mod.rs index 7bc41227..a0e67b4d 100644 --- a/grovedb/src/query/mod.rs +++ b/grovedb/src/query/mod.rs @@ -134,7 +134,7 @@ impl PathQuery { Self { path, query } } - /// The max depth od the query, this is the maximum layers we could get back + /// The max depth of the query, this is the maximum layers we could get back /// from grovedb pub fn max_depth(&self) -> u32 { self.query.query.max_depth() From e8843ee5f87dcb1dcf97396704b8bf2f98741953 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 20 Aug 2024 15:55:52 +0700 Subject: [PATCH 3/5] improvements --- grovedb/build.rs | 4 +-- grovedb/src/query/mod.rs | 39 ++++++++++++++++++++++-- merk/src/proofs/query/mod.rs | 59 +++++++++++++++++++++++++----------- 3 files changed, 81 insertions(+), 21 deletions(-) diff --git a/grovedb/build.rs b/grovedb/build.rs index ae9e61c1..f5792754 100644 --- a/grovedb/build.rs +++ b/grovedb/build.rs @@ -14,9 +14,9 @@ fn main() { if !grovedbg_zip_path.exists() { let response = reqwest::blocking::get(format!( - "https://github.com/dashpay/grovedbg/releases/download/{GROVEDBG_VERSION}/\ + "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(); diff --git a/grovedb/src/query/mod.rs b/grovedb/src/query/mod.rs index 7bc41227..53012409 100644 --- a/grovedb/src/query/mod.rs +++ b/grovedb/src/query/mod.rs @@ -136,7 +136,9 @@ impl PathQuery { /// The max depth od the query, this is the maximum layers we could get back /// from grovedb - pub fn max_depth(&self) -> u32 { + /// 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 { self.query.query.max_depth() } @@ -1640,7 +1642,7 @@ mod tests { }, }; - assert_eq!(path_query.max_depth(), 4); + assert_eq!(path_query.max_depth(), Some(4)); { let path = vec![]; @@ -1718,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); + } } diff --git a/merk/src/proofs/query/mod.rs b/merk/src/proofs/query/mod.rs index 4cf25273..74fe0191 100644 --- a/merk/src/proofs/query/mod.rs +++ b/merk/src/proofs/query/mod.rs @@ -74,13 +74,28 @@ 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) -> u32 { - let subquery_path_depth = self - .subquery_path - .as_ref() - .map_or(0, |path| path.len() as u32); - let subquery_depth = self.subquery.as_ref().map_or(0, |query| query.max_depth()); - subquery_path_depth + subquery_depth + pub fn max_depth(&self) -> Option { + 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 { + if recursion_limit == 0 { + return None; + } + let subquery_path_depth = self.subquery_path.as_ref().map_or(Some(0), |path| { + if path.len() > 255 { + 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) } } @@ -493,19 +508,29 @@ impl Query { /// 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) -> u32 { - let default_subquery_branch_depth = self.default_subquery_branch.max_depth(); + pub fn max_depth(&self) -> Option { + 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 { + 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(0, |condition_subqueries| { - condition_subqueries - .values() - .map(|conditional_subquery_branch| conditional_subquery_branch.max_depth()) - .max() - .unwrap_or_default() - }); - 1 + default_subquery_branch_depth.max(conditional_subquery_branches_max_depth) + .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)) } } From 3e4f6d5fb67c59db2e1e3d1305b856e7e34d7be1 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 20 Aug 2024 16:03:34 +0700 Subject: [PATCH 4/5] fix --- merk/src/proofs/query/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/merk/src/proofs/query/mod.rs b/merk/src/proofs/query/mod.rs index 74fe0191..de66403a 100644 --- a/merk/src/proofs/query/mod.rs +++ b/merk/src/proofs/query/mod.rs @@ -86,7 +86,7 @@ impl SubqueryBranch { return None; } let subquery_path_depth = self.subquery_path.as_ref().map_or(Some(0), |path| { - if path.len() > 255 { + if path.len() > u16::MAX as usize { None } else { Some(path.len() as u16) From 3de4649f58d02a7bec29072762e6e51ffbaf4593 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 20 Aug 2024 16:04:09 +0700 Subject: [PATCH 5/5] fix --- merk/src/proofs/query/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/merk/src/proofs/query/mod.rs b/merk/src/proofs/query/mod.rs index de66403a..8bbb07e9 100644 --- a/merk/src/proofs/query/mod.rs +++ b/merk/src/proofs/query/mod.rs @@ -86,10 +86,11 @@ impl SubqueryBranch { return None; } let subquery_path_depth = self.subquery_path.as_ref().map_or(Some(0), |path| { - if path.len() > u16::MAX as usize { + let path_len = path.len(); + if path_len > u16::MAX as usize { None } else { - Some(path.len() as u16) + Some(path_len as u16) } })?; let subquery_depth = self.subquery.as_ref().map_or(Some(0), |query| {