diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 99c3a8708..94464040d 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -9,7 +9,11 @@ use std::{ }; pub use merk::proofs::{query::QueryItem, Query}; -use merk::{self, proofs::query::Map, Merk}; +use merk::{ + self, + proofs::query::Map, + Merk, +}; use rs_merkle::{algorithms::Sha256, Hasher, MerkleProof, MerkleTree}; use storage::{ rocksdb_storage::{PrefixedRocksDbStorage, PrefixedRocksDbStorageError}, @@ -374,89 +378,108 @@ impl GroveDb { Ok(proof_result) } - // Validates proof structure and returns the root hash - // and query result - // pub fn execute_proof( - // path: &[&[u8]], - // proofs: &mut Vec>, - // ) -> Result<([u8; 32], Map), Error> { - // if proofs.len() < 2 { - // return Err(Error::InvalidProof("Proof length should be 2 or more")); - // } - // - // if proofs.len() - 2 != path.len() { - // return Err(Error::InvalidProof( - // "Proof length should be two greater than path", - // )); - // } - // - // let root_leaf_keys: HashMap, usize> = - // bincode::deserialize(&proofs.pop().unwrap()[..]) - // .map_err(|_| Error::CorruptedData(String::from("unable to deserialize element")))?; - // - // let mut proof_iterator = proofs.iter(); - // let reverse_path_iterator = path.iter().rev(); - // - // let leaf_proof = proof_iterator - // .next() - // .expect("Constraint checks above enforces leaf proof must exist"); - // - // let (mut last_root_hash, leaf_result_map) = match merk::execute_proof(&leaf_proof[..]) { - // Ok(result) => Ok(result), - // Err(_) => Err(Error::InvalidProof("Invalid proof element")), - // }?; - // - // let mut proof_path_zip = proof_iterator.zip(reverse_path_iterator).peekable(); - // let mut root_hash: Option<[u8; 32]> = None; - // - // while let Some((proof, key)) = proof_path_zip.next() { - // if proof_path_zip.peek().is_some() { - // // Non root proof, validate that the proof is valid and - // // the result map contains the last subtree root hash i.e the previous - // // subtree is a child of this tree - // let proof_result = match merk::execute_proof(&proof[..]) { - // Ok(result) => Ok(result), - // Err(_) => Err(Error::InvalidProof("Invalid proof element")), - // }?; - // let result_map = proof_result.1; - // - // let elem: Element = - // bincode::deserialize(result_map.get(key).unwrap().unwrap()).unwrap(); - // let merk_root_hash = match elem { - // Element::Tree(hash) => Ok(hash), - // _ => Err(Error::InvalidProof( - // "Intermediate proofs should be for trees", - // )), - // }?; - // - // if merk_root_hash != last_root_hash { - // return Err(Error::InvalidProof("Bad path")); - // } - // - // last_root_hash = proof_result.0; - // } else { - // // Last proof (root proof) - // let root_proof = match MerkleProof::::try_from(&proof[..]) { - // Ok(root_proof) => Ok(root_proof), - // Err(_) => Err(Error::InvalidProof("Invalid proof element")), - // }?; - // let a: [u8; 32] = last_root_hash; - // root_hash = - // Some( - // match root_proof.root(&[root_leaf_keys[*key]], &[a], root_leaf_keys.len()) { - // Ok(hash) => Ok(hash), - // Err(_) => Err(Error::InvalidProof("Invalid proof element")), - // }?, - // ); - // } - // } - // - // return if let Some(hash) = root_hash { - // Ok((hash, leaf_result_map)) - // } else { - // Err(Error::InvalidProof("Invalid proof element")) - // }; - // } + pub fn execute_proof(proof: Proof) -> Result<([u8; 32], HashMap, Map>), Error> { + // Required to execute the root proof + let mut root_keys_index: Vec = Vec::new(); + let mut root_hashes: Vec<[u8; 32]> = Vec::new(); + + // Collects the result map for each query + let mut result_map: HashMap, Map> = HashMap::new(); + + for path in proof.query_paths { + // For each query path, get the result map after execution + // and store hash + index for later root proof execution + let root_key = path[0]; + let (hash, proof_result_map) = GroveDb::execute_path(path, &proof.proofs)?; + let compressed_root_key_path = GroveDb::compress_subtree_key(&[], Some(&root_key)); + let compressed_query_path = GroveDb::compress_subtree_key(path, None); + + let index = proof + .root_leaf_keys + .get(&compressed_root_key_path) + .ok_or(Error::InvalidPath("Bad path"))?; + if !root_keys_index.contains(&index) { + root_keys_index.push(*index); + root_hashes.push(hash); + } + + result_map.insert(compressed_query_path, proof_result_map); + } + + let root_proof = match MerkleProof::::try_from(proof.root_proof) { + Ok(proof) => Ok(proof), + Err(_) => Err(Error::InvalidProof("Invalid proof element")), + }?; + + let root_hash = + match root_proof.root(&root_keys_index, &root_hashes, proof.root_leaf_keys.len()) { + Ok(hash) => Ok(hash), + Err(_) => Err(Error::InvalidProof("Invalid proof element")), + }?; + + Ok((root_hash, result_map)) + } + + // Given a query path and a set of proofs + // execute_path validates that the nodes represented by the paths + // are connected to one another i.e root hash of child node is in parent node + // at the correct key. + // If path is valid, it returns the root hash of topmost merk and result map of + // leaf merk. + fn execute_path( + path: &[&[u8]], + proofs: &HashMap, Vec>, + ) -> Result<([u8; 32], Map), Error> { + let compressed_path = GroveDb::compress_subtree_key(path, None); + let proof = proofs + .get(&compressed_path) + .ok_or(Error::InvalidPath("Bad path"))?; + + // Execute the leaf merk proof + let (mut last_root_hash, result_map) = match merk::execute_proof(&proof[..]) { + Ok(result) => Ok(result), + Err(_) => Err(Error::InvalidPath("Invalid proof element")), + }?; + + // Validate the path + let mut split_path = path.split_last(); + while let Some((key, path_slice)) = split_path { + if !path_slice.is_empty() { + let compressed_path = GroveDb::compress_subtree_key(path_slice, None); + let proof = proofs + .get(&compressed_path) + .ok_or(Error::InvalidPath("Bad path"))?; + + let proof_result = match merk::execute_proof(&proof[..]) { + Ok(result) => Ok(result), + Err(_) => Err(Error::InvalidPath("Invalid proof element")), + }?; + + let result_map = proof_result.1; + // TODO: Handle the error better here + let elem: Element = + bincode::deserialize(result_map.get(key).unwrap().unwrap()).unwrap(); + let merk_root_hash = match elem { + Element::Tree(hash) => Ok(hash), + _ => Err(Error::InvalidProof( + "Intermediate proofs should be for trees", + )), + }?; + + if merk_root_hash != last_root_hash { + return Err(Error::InvalidProof("Bad path")); + } + + last_root_hash = proof_result.0; + } else { + break; + } + + split_path = path_slice.split_last(); + } + + Ok((last_root_hash, result_map)) + } /// Method to propagate updated subtree root hashes up to GroveDB root fn propagate_changes(&mut self, path: &[&[u8]]) -> Result<(), Error> { diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index d9ef92fbf..4c9da3c8f 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -456,123 +456,134 @@ fn test_proof_construction() { assert_eq!(proof.root_leaf_keys[&another_test_leaf_root_key], 1); } -// #[test] -// fn test_successful_proof_verification() { -// 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"innertree1.1".to_vec(), -// Element::empty_tree(), -// ) -// .expect("successful subtree insert"); -// -// temp_db -// .insert(&[TEST_LEAF], b"innertree2".to_vec(), Element::empty_tree()) -// .expect("successful subtree insert"); -// -// temp_db -// .insert( -// &[TEST_LEAF, b"innertree", b"innertree1.1"], -// b"key1".to_vec(), -// Element::Item(b"value1".to_vec()), -// ) -// .expect("successful item insert"); -// -// temp_db -// .insert( -// &[TEST_LEAF, b"innertree2"], -// b"key1".to_vec(), -// Element::Item(b"value2".to_vec()), -// ) -// .expect("successful item insert"); -// -// // dbg!(temp_db.root_tree.root().unwrap()); -// -// let mut proof_query = Query::new(); -// proof_query.insert_key(b"key1".to_vec()); -// let mut proof = temp_db -// .proof(&[TEST_LEAF, b"innertree", b"innertree1.1"], proof_query) -// .unwrap(); -// -// let (root_hash, result_map) = -// GroveDb::execute_proof(&[TEST_LEAF, b"innertree", b"innertree1.1"], -// &mut proof).unwrap(); -// -// // Check that the root hash matches -// assert_eq!(temp_db.root_tree.root().unwrap(), root_hash); -// -// // Check that the result map is correct -// let elem: Element = -// bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); -// assert_eq!(elem, Element::Item(b"value1".to_vec())); -// } -// -// #[test] -// #[should_panic] -// fn test_malicious_proof_verification() { -// // Verification should detect when the proofs don't follow a valid path -// // i.e. root - leaf (with each individual merk connected to their parent -// by // their root hash) Grovedb enforces a valid path, so will manually -// // construct a malicious proof -// -// // 4 trees, merk_one, merk_two, merk_three, root -// // root references m3, m3 references m1 (instead of m2), m2 references m1 -// // m3 breaks the chain and as such the proof should not be considered -// valid -// -// let mut proofs: Vec> = Vec::new(); -// -// // Merk One -// let mut merk_one = TempMerk::new(); -// let value_element = Element::Item(b"value1".to_vec()); -// value_element.insert(&mut merk_one, b"key1".to_vec()); -// -// let mut proof_query = Query::new(); -// proof_query.insert_key(b"key1".to_vec()); -// proofs.push(merk_one.prove(proof_query).unwrap()); -// -// // Merk Two -// let mut merk_two = TempMerk::new(); -// let merk_two_element = Element::Tree(merk_one.root_hash()); -// merk_two_element.insert(&mut merk_two, b"innertree-2".to_vec()); -// -// let mut proof_query = Query::new(); -// proof_query.insert_key(b"innertree-2".to_vec()); -// proofs.push(merk_two.prove(proof_query).unwrap()); -// -// // Merk Three -// let mut merk_three = TempMerk::new(); -// let merk_three_element = Element::Tree(merk_one.root_hash()); -// merk_three_element.insert(&mut merk_three, b"innertree".to_vec()); -// -// let mut proof_query = Query::new(); -// proof_query.insert_key(b"innertree".to_vec()); -// proofs.push(merk_three.prove(proof_query).unwrap()); -// -// let another_test_leaf_merk = TempMerk::new(); -// -// // Root Tree -// let leaves = [merk_three.root_hash(), -// another_test_leaf_merk.root_hash()]; let root_tree = -// MerkleTree::::from_leaves(&leaves); proofs.push(root_tree.proof(& -// vec![0]).to_bytes()); -// -// let (root_hash, result_map) = -// GroveDb::execute_proof(&[TEST_LEAF, b"innertree", b"innertree-2"], -// &mut proofs).unwrap(); -// -// // Check that the root hash matches -// assert_eq!(root_tree.root().unwrap(), root_hash); -// -// let elem: Element = -// bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); -// assert_eq!(elem, Element::Item(b"value1".to_vec())); -// } +#[test] +fn test_successful_proof_verification() { + // Build a grovedb database + // Tree Structure + // root + // test_leaf + // innertree + // k1,v1 + // k2,v2 + // another_test_leaf + // innertree2 + // k3,v3 + // innertree3 + // k4,v4 + + // Insert elements into grovedb instance + let mut temp_db = make_grovedb(); + // Insert level 1 nodes + temp_db + .insert(&[TEST_LEAF], b"innertree".to_vec(), Element::empty_tree()) + .expect("successful subtree insert"); + temp_db + .insert( + &[ANOTHER_TEST_LEAF], + b"innertree2".to_vec(), + Element::empty_tree(), + ) + .expect("successful subtree insert"); + temp_db + .insert( + &[ANOTHER_TEST_LEAF], + b"innertree3".to_vec(), + Element::empty_tree(), + ) + .expect("successful subtree insert"); + // Insert level 2 nodes + temp_db + .insert( + &[TEST_LEAF, b"innertree"], + b"key1".to_vec(), + Element::Item(b"value1".to_vec()), + ) + .expect("successful subtree insert"); + temp_db + .insert( + &[TEST_LEAF, b"innertree"], + b"key2".to_vec(), + Element::Item(b"value2".to_vec()), + ) + .expect("successful subtree insert"); + temp_db + .insert( + &[ANOTHER_TEST_LEAF, b"innertree2"], + b"key3".to_vec(), + Element::Item(b"value3".to_vec()), + ) + .expect("successful subtree insert"); + temp_db + .insert( + &[ANOTHER_TEST_LEAF, b"innertree3"], + b"key4".to_vec(), + Element::Item(b"value4".to_vec()), + ) + .expect("successful subtree insert"); + + // Single query proof verification + let mut path_one_query = Query::new(); + path_one_query.insert_key(b"key1".to_vec()); + path_one_query.insert_key(b"key2".to_vec()); + + let proof = temp_db + .proof(vec![ProofQuery { + path: &[TEST_LEAF, b"innertree"], + query: path_one_query, + }]) + .unwrap(); + + // Assert correct root hash + let (root_hash, result_maps) = GroveDb::execute_proof(proof).unwrap(); + assert_eq!(temp_db.root_tree.root().unwrap(), root_hash); + + // Assert correct result object + // Proof query was for two keys key1 and key2 + let path_as_vec = GroveDb::compress_subtree_key(&[TEST_LEAF, b"innertree"], None); + let result_map = result_maps.get(&path_as_vec).unwrap(); + let elem_1: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); + let elem_2: Element = bincode::deserialize(result_map.get(b"key2").unwrap().unwrap()).unwrap(); + assert_eq!(elem_1, Element::Item(b"value1".to_vec())); + assert_eq!(elem_2, Element::Item(b"value2".to_vec())); + + // Multi query proof verification + let mut path_two_query = Query::new(); + path_two_query.insert_key(b"key4".to_vec()); + + let mut path_three_query = Query::new(); + path_three_query.insert_key(b"key3".to_vec()); + + // Get grovedb proof + let proof = temp_db + .proof(vec![ + ProofQuery { + path: &[ANOTHER_TEST_LEAF, b"innertree3"], + query: path_two_query, + }, + ProofQuery { + path: &[ANOTHER_TEST_LEAF, b"innertree2"], + query: path_three_query, + }, + ]) + .unwrap(); + + // Assert correct root hash + let (root_hash, result_maps) = GroveDb::execute_proof(proof).unwrap(); + assert_eq!(temp_db.root_tree.root().unwrap(), root_hash); + + // Assert correct result object + let path_one_as_vec = GroveDb::compress_subtree_key(&[ANOTHER_TEST_LEAF, b"innertree3"], None); + let result_map = result_maps.get(&path_one_as_vec).unwrap(); + let elem: Element = bincode::deserialize(result_map.get(b"key4").unwrap().unwrap()).unwrap(); + assert_eq!(elem, Element::Item(b"value4".to_vec())); + let path_two_as_vec = GroveDb::compress_subtree_key(&[ANOTHER_TEST_LEAF, b"innertree2"], None); + let result_map = result_maps.get(&path_two_as_vec).unwrap(); + let elem: Element = bincode::deserialize(result_map.get(b"key3").unwrap().unwrap()).unwrap(); + assert_eq!(elem, Element::Item(b"value3".to_vec())); +} + +#[test] fn test_checkpoint() { let mut db = make_grovedb(); let element1 = Element::Item(b"ayy".to_vec());