diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 41d980832..111336d51 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -8,6 +8,7 @@ use std::{ rc::Rc, }; +pub use merk::proofs::{query::QueryItem, Query}; use merk::{self, Merk}; use rs_merkle::{algorithms::Sha256, MerkleTree}; use storage::{ @@ -259,8 +260,50 @@ impl GroveDb { Err(Error::ReferenceLimit) } - pub fn proof(&self) -> ! { - todo!() + pub fn proof(&self, path: &[&[u8]], proof_query: Query) -> Result>, Error> { + let mut proofs: Vec> = Vec::new(); + + // First prove the query + proofs.push(self.prove_item(path, proof_query)?); + + // Next prove the query path + let mut split_path = path.split_last(); + while let Some((key, path_slice)) = split_path { + if path_slice.is_empty() { + // Get proof for root tree at current key + let root_key_index = self + .root_leaf_keys + .get(*key) + .ok_or(Error::InvalidPath("root key not found"))?; + proofs.push(self.root_tree.proof(&[*root_key_index]).to_bytes()); + } else { + let mut path_query = Query::new(); + path_query.insert_item(QueryItem::Key(key.to_vec())); + proofs.push(self.prove_item(path_slice, path_query)?); + } + split_path = path_slice.split_last(); + } + + // Append the root leaf keys hash map to proof to provide context when verifying + // proof + let aux_data = bincode::serialize(&self.root_leaf_keys) + .map_err(|_| Error::CorruptedData(String::from("unable to deserialize element")))?; + proofs.push(aux_data); + + Ok(proofs) + } + + fn prove_item(&self, path: &[&[u8]], proof_query: Query) -> Result, Error> { + let merk = self + .subtrees + .get(&Self::compress_path(path, None)) + .ok_or(Error::InvalidPath("no subtree found under that path"))?; + + let proof_result = merk + .prove(proof_query) + .expect("should prove both inclusion and absence"); + + Ok(proof_result) } /// Method to propagate updated subtree root hashes up to GroveDB root diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 4b92ce4d8..c83042edc 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -1,5 +1,6 @@ use std::ops::{Deref, DerefMut}; +use merk::test_utils::TempMerk; use tempdir::TempDir; use super::*; @@ -240,6 +241,72 @@ fn test_root_tree_leafs_are_noted() { assert_eq!(db.root_tree.leaves_len(), 2); } +#[test] +fn test_proof_construction() { + let mut temp_db = make_grovedb(); + temp_db + .insert(&[TEST_LEAF], b"innertree".to_vec(), Element::empty_tree()) + .expect("successful subtree insert"); + temp_db + .insert( + &[TEST_LEAF, b"innertree"], + b"key1".to_vec(), + Element::Item(b"value1".to_vec()), + ) + .expect("successful item insert"); + temp_db + .insert( + &[TEST_LEAF, b"innertree"], + b"key2".to_vec(), + Element::Item(b"value2".to_vec()), + ) + .expect("successful item insert"); + + // Manually build the ads structures + let mut inner_tree_merk = TempMerk::new(); + let value_element = Element::Item(b"value1".to_vec()); + value_element.insert(&mut inner_tree_merk, b"key1".to_vec()); + let value_element = Element::Item(b"value2".to_vec()); + value_element.insert(&mut inner_tree_merk, b"key2".to_vec()); + + let mut test_leaf_merk = TempMerk::new(); + let inner_tree_root_element = Element::Tree(inner_tree_merk.root_hash()); + inner_tree_root_element.insert(&mut test_leaf_merk, b"innertree".to_vec()); + + let another_test_leaf_merk = TempMerk::new(); + + let leaves = [ + test_leaf_merk.root_hash(), + another_test_leaf_merk.root_hash(), + ]; + let root_tree = MerkleTree::::from_leaves(&leaves); + + // Generate grove db proof + let mut proof_query = Query::new(); + proof_query.insert_key(b"key1".to_vec()); + let proof = temp_db + .proof(&[TEST_LEAF, b"innertree"], proof_query) + .expect("Successful proof generation"); + + assert_eq!(proof.len(), 4); + + let mut proof_query = Query::new(); + proof_query.insert_key(b"key1".to_vec()); + assert_eq!(proof[0], inner_tree_merk.prove(proof_query).unwrap()); + + let mut proof_query = Query::new(); + proof_query.insert_key(b"innertree".to_vec()); + assert_eq!(proof[1], test_leaf_merk.prove(proof_query).unwrap()); + + assert_eq!(proof[2], root_tree.proof(&vec![0]).to_bytes()); + + let root_leaf_keys: HashMap, usize> = bincode::deserialize(&proof[3][..]).unwrap(); + assert_eq!(root_leaf_keys.len(), temp_db.root_leaf_keys.len()); + for (key, index) in &root_leaf_keys { + assert_eq!(root_leaf_keys[key], temp_db.root_leaf_keys[key]); + } +} + #[test] fn test_checkpoint() { let mut db = make_grovedb();