Skip to content

Commit

Permalink
feat: queries proofs grovedbg update (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
fominok authored Jul 26, 2024
1 parent 875d6c8 commit 96dd04e
Show file tree
Hide file tree
Showing 6 changed files with 480 additions and 39 deletions.
323 changes: 286 additions & 37 deletions grovedb/src/debugger.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
//! GroveDB debugging support module.

use std::{fs, net::Ipv4Addr, sync::Weak};
use std::{collections::BTreeMap, fs, sync::Weak};

use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::post, Json, Router};
use grovedb_merk::debugger::NodeDbg;
use grovedb_merk::{
debugger::NodeDbg,
proofs::{Decoder, Node, Op},
TreeFeatureType,
};
use grovedb_path::SubtreePath;
use grovedb_version::version::GroveVersion;
use grovedbg_types::{NodeFetchRequest, NodeUpdate, Path};
use grovedbg_types::{
MerkProofNode, MerkProofOp, NodeFetchRequest, NodeUpdate, Path, PathQuery, Query, QueryItem,
SizedQuery, SubqueryBranch,
};
use indexmap::IndexMap;
use tokio::{
net::ToSocketAddrs,
sync::mpsc::{self, Sender},
};
use tower_http::services::ServeDir;

use crate::{reference_path::ReferencePathType, GroveDb};
use crate::{
operations::proof::{GroveDBProof, LayerProof, ProveOptions},
reference_path::ReferencePathType,
GroveDb,
};

const GROVEDBG_ZIP: [u8; include_bytes!(concat!(env!("OUT_DIR"), "/grovedbg.zip")).len()] =
*include_bytes!(concat!(env!("OUT_DIR"), "/grovedbg.zip"));
Expand All @@ -36,6 +48,7 @@ where
let app = Router::new()
.route("/fetch_node", post(fetch_node))
.route("/fetch_root_node", post(fetch_root_node))
.route("/execute_path_query", post(execute_path_query))
.fallback_service(ServeDir::new(grovedbg_www))
.with_state((shutdown_send, grovedb));

Expand Down Expand Up @@ -123,68 +136,304 @@ async fn fetch_root_node(
}
}

fn node_to_update(
path: Path,
NodeDbg {
key,
value,
left_child,
right_child,
}: NodeDbg,
) -> Result<NodeUpdate, crate::Error> {
// todo: GroveVersion::latest() to actual version
let grovedb_element = crate::Element::deserialize(&value, GroveVersion::latest())?;
async fn execute_path_query(
State((shutdown, grovedb)): State<(Sender<()>, Weak<GroveDb>)>,
Json(json_path_query): Json<PathQuery>,
) -> Result<Json<grovedbg_types::Proof>, AppError> {
let Some(db) = grovedb.upgrade() else {
shutdown.send(()).await.ok();
return Err(AppError::Closed);
};

let path_query = path_query_to_grovedb(json_path_query);

let grovedb_proof = db
.prove_internal(&path_query, None, GroveVersion::latest())
.unwrap()?;
Ok(Json(proof_to_grovedbg(grovedb_proof)?))
}

fn proof_to_grovedbg(proof: GroveDBProof) -> Result<grovedbg_types::Proof, crate::Error> {
match proof {
GroveDBProof::V0(p) => Ok(grovedbg_types::Proof {
root_layer: proof_layer_to_grovedbg(p.root_layer)?,
prove_options: prove_options_to_grovedbg(p.prove_options),
}),
}
}

fn proof_layer_to_grovedbg(
proof_layer: LayerProof,
) -> Result<grovedbg_types::ProofLayer, crate::Error> {
Ok(grovedbg_types::ProofLayer {
merk_proof: merk_proof_to_grovedbg(&proof_layer.merk_proof)?,
lower_layers: proof_layer
.lower_layers
.into_iter()
.map(|(k, v)| proof_layer_to_grovedbg(v).map(|layer| (k, layer)))
.collect::<Result<BTreeMap<Vec<u8>, grovedbg_types::ProofLayer>, crate::Error>>()?,
})
}

fn merk_proof_to_grovedbg(merk_proof: &[u8]) -> Result<Vec<MerkProofOp>, crate::Error> {
let decoder = Decoder::new(merk_proof);
decoder
.map(|op_result| {
op_result
.map_err(crate::Error::MerkError)
.and_then(merk_proof_op_to_grovedbg)
})
.collect::<Result<Vec<MerkProofOp>, _>>()
}
fn merk_proof_op_to_grovedbg(op: Op) -> Result<MerkProofOp, crate::Error> {
Ok(match op {
Op::Push(node) => MerkProofOp::Push(merk_proof_node_to_grovedbg(node)?),
Op::PushInverted(node) => MerkProofOp::PushInverted(merk_proof_node_to_grovedbg(node)?),
Op::Parent => MerkProofOp::Parent,
Op::Child => MerkProofOp::Child,
Op::ParentInverted => MerkProofOp::ParentInverted,
Op::ChildInverted => MerkProofOp::ChildInverted,
})
}

fn merk_proof_node_to_grovedbg(node: Node) -> Result<MerkProofNode, crate::Error> {
Ok(match node {
Node::Hash(hash) => MerkProofNode::Hash(hash),
Node::KVHash(hash) => MerkProofNode::KVHash(hash),
Node::KVDigest(key, hash) => MerkProofNode::KVDigest(key, hash),
Node::KV(key, value) => {
let element = crate::Element::deserialize(&value, GroveVersion::latest())?;
MerkProofNode::KV(key, element_to_grovedbg(element))
}
Node::KVValueHash(key, value, hash) => {
let element = crate::Element::deserialize(&value, GroveVersion::latest())?;
MerkProofNode::KVValueHash(key, element_to_grovedbg(element), hash)
}
Node::KVValueHashFeatureType(key, value, hash, TreeFeatureType::BasicMerkNode) => {
let element = crate::Element::deserialize(&value, GroveVersion::latest())?;
MerkProofNode::KVValueHashFeatureType(
key,
element_to_grovedbg(element),
hash,
grovedbg_types::TreeFeatureType::BasicMerkNode,
)
}
Node::KVValueHashFeatureType(key, value, hash, TreeFeatureType::SummedMerkNode(sum)) => {
let element = crate::Element::deserialize(&value, GroveVersion::latest())?;
MerkProofNode::KVValueHashFeatureType(
key,
element_to_grovedbg(element),
hash,
grovedbg_types::TreeFeatureType::SummedMerkNode(sum),
)
}
Node::KVRefValueHash(key, value, hash) => {
let element = crate::Element::deserialize(&value, GroveVersion::latest())?;
MerkProofNode::KVRefValueHash(key, element_to_grovedbg(element), hash)
}
})
}

fn prove_options_to_grovedbg(options: ProveOptions) -> grovedbg_types::ProveOptions {
grovedbg_types::ProveOptions {
decrease_limit_on_empty_sub_query_result: options.decrease_limit_on_empty_sub_query_result,
}
}

fn path_query_to_grovedb(query: PathQuery) -> crate::PathQuery {
let PathQuery {
path,
query:
SizedQuery {
limit,
offset,
query: inner_query,
},
} = query;

crate::PathQuery {
path,
query: crate::SizedQuery {
query: query_to_grovedb(inner_query),
limit,
offset,
},
}
}

let element = match grovedb_element {
crate::Element::Item(value, ..) => grovedbg_types::Element::Item { value },
crate::Element::Tree(root_key, ..) => grovedbg_types::Element::Subtree { root_key },
crate::Element::Reference(ReferencePathType::AbsolutePathReference(path), ..) => {
grovedbg_types::Element::AbsolutePathReference { path }
fn query_to_grovedb(query: Query) -> crate::Query {
crate::Query {
items: query.items.into_iter().map(query_item_to_grovedb).collect(),
default_subquery_branch: subquery_branch_to_grovedb(query.default_subquery_branch),
conditional_subquery_branches: conditional_subquery_branches_to_grovedb(
query.conditional_subquery_branches,
),
left_to_right: query.left_to_right,
}
}

fn conditional_subquery_branches_to_grovedb(
conditional_subquery_branches: Vec<(QueryItem, SubqueryBranch)>,
) -> Option<IndexMap<crate::QueryItem, grovedb_merk::proofs::query::SubqueryBranch>> {
if conditional_subquery_branches.is_empty() {
None
} else {
Some(
conditional_subquery_branches
.into_iter()
.map(|(item, branch)| {
(
query_item_to_grovedb(item),
subquery_branch_to_grovedb(branch),
)
})
.collect(),
)
}
}

fn subquery_branch_to_grovedb(
subquery_branch: SubqueryBranch,
) -> grovedb_merk::proofs::query::SubqueryBranch {
grovedb_merk::proofs::query::SubqueryBranch {
subquery_path: subquery_branch.subquery_path,
subquery: subquery_branch
.subquery
.map(|q| Box::new(query_to_grovedb(*q))),
}
}

fn query_item_to_grovedb(item: QueryItem) -> crate::QueryItem {
match item {
QueryItem::Key(x) => crate::QueryItem::Key(x),
QueryItem::Range { start, end } => crate::QueryItem::Range(start..end),
QueryItem::RangeInclusive { start, end } => crate::QueryItem::RangeInclusive(start..=end),
QueryItem::RangeFull => crate::QueryItem::RangeFull(..),
QueryItem::RangeFrom(x) => crate::QueryItem::RangeFrom(x..),
QueryItem::RangeTo(x) => crate::QueryItem::RangeTo(..x),
QueryItem::RangeToInclusive(x) => crate::QueryItem::RangeToInclusive(..=x),
QueryItem::RangeAfter(x) => crate::QueryItem::RangeAfter(x..),
QueryItem::RangeAfterTo { after, to } => crate::QueryItem::RangeAfterTo(after..to),
QueryItem::RangeAfterToInclusive { after, to } => {
crate::QueryItem::RangeAfterToInclusive(after..=to)
}
}
}

fn element_to_grovedbg(element: crate::Element) -> grovedbg_types::Element {
match element {
crate::Element::Item(value, element_flags) => grovedbg_types::Element::Item {
value,
element_flags,
},
crate::Element::Tree(root_key, element_flags) => grovedbg_types::Element::Subtree {
root_key,
element_flags,
},
crate::Element::Reference(
ReferencePathType::AbsolutePathReference(path),
_,
element_flags,
) => grovedbg_types::Element::AbsolutePathReference {
path,
element_flags,
},
crate::Element::Reference(
ReferencePathType::UpstreamRootHeightReference(n_keep, path_append),
..,
_,
element_flags,
) => grovedbg_types::Element::UpstreamRootHeightReference {
n_keep: n_keep.into(),
path_append,
element_flags,
},
crate::Element::Reference(
ReferencePathType::UpstreamRootHeightWithParentPathAdditionReference(
n_keep,
path_append,
),
..,
_,
element_flags,
) => grovedbg_types::Element::UpstreamRootHeightWithParentPathAdditionReference {
n_keep: n_keep.into(),
path_append,
element_flags,
},
crate::Element::Reference(
ReferencePathType::UpstreamFromElementHeightReference(n_remove, path_append),
..,
_,
element_flags,
) => grovedbg_types::Element::UpstreamFromElementHeightReference {
n_remove: n_remove.into(),
path_append,
element_flags,
},
crate::Element::Reference(ReferencePathType::CousinReference(swap_parent), ..) => {
grovedbg_types::Element::CousinReference { swap_parent }
}
crate::Element::Reference(ReferencePathType::RemovedCousinReference(swap_parent), ..) => {
grovedbg_types::Element::RemovedCousinReference { swap_parent }
}
crate::Element::Reference(ReferencePathType::SiblingReference(sibling_key), ..) => {
grovedbg_types::Element::SiblingReference { sibling_key }
}
crate::Element::SumItem(value, _) => grovedbg_types::Element::SumItem { value },
crate::Element::SumTree(root_key, sum, _) => {
grovedbg_types::Element::Sumtree { root_key, sum }
}
};
crate::Element::Reference(
ReferencePathType::CousinReference(swap_parent),
_,
element_flags,
) => grovedbg_types::Element::CousinReference {
swap_parent,
element_flags,
},
crate::Element::Reference(
ReferencePathType::RemovedCousinReference(swap_parent),
_,
element_flags,
) => grovedbg_types::Element::RemovedCousinReference {
swap_parent,
element_flags,
},
crate::Element::Reference(
ReferencePathType::SiblingReference(sibling_key),
_,
element_flags,
) => grovedbg_types::Element::SiblingReference {
sibling_key,
element_flags,
},
crate::Element::SumItem(value, element_flags) => grovedbg_types::Element::SumItem {
value,
element_flags,
},
crate::Element::SumTree(root_key, sum, element_flags) => grovedbg_types::Element::Sumtree {
root_key,
sum,
element_flags,
},
}
}

fn node_to_update(
path: Path,
NodeDbg {
key,
value,
left_child,
right_child,
value_hash,
kv_digest_hash,
feature_type,
}: NodeDbg,
) -> Result<NodeUpdate, crate::Error> {
// todo: GroveVersion::latest() to actual version
let grovedb_element = crate::Element::deserialize(&value, GroveVersion::latest())?;

let element = element_to_grovedbg(grovedb_element);

Ok(NodeUpdate {
path,
key,
element,
left_child,
right_child,
feature_type: match feature_type {
TreeFeatureType::BasicMerkNode => grovedbg_types::TreeFeatureType::BasicMerkNode,
TreeFeatureType::SummedMerkNode(x) => {
grovedbg_types::TreeFeatureType::SummedMerkNode(x)
}
},
value_hash,
kv_digest_hash,
})
}
2 changes: 1 addition & 1 deletion grovedb/src/operations/proof/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl GroveDb {
}

/// Generates a proof
fn prove_internal(
pub(crate) fn prove_internal(
&self,
path_query: &PathQuery,
prove_options: Option<ProveOptions>,
Expand Down
1 change: 1 addition & 0 deletions grovedbg-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ repository = "https://github.com/dashpay/grovedb"

[dependencies]
serde = { version = "1.0.201", features = ["derive"] }
serde_with = { version = "3.9.0", features = ["base64"] }
Loading

0 comments on commit 96dd04e

Please sign in to comment.