From 89bc83d1161f735a45db1667041bcc45c4156259 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 8 Dec 2021 05:16:41 +0100 Subject: [PATCH 01/20] added invalid proof error type --- grovedb/src/lib.rs | 26 +++++++++++++++++++++++++- grovedb/src/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index dc45e7a60..960245729 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -10,8 +10,9 @@ use std::{ }; pub use merk::proofs::query::QueryItem; -use merk::{self, proofs::Query, rocksdb, Merk}; +use merk::{self, proofs::Query, rocksdb, Merk, execute_proof}; use rs_merkle::{algorithms::Sha256, MerkleTree}; +use merk::proofs::query::Map; use subtree::Element; /// Limit of possible indirections @@ -36,6 +37,8 @@ pub enum Error { CyclicReference, #[error("reference hops limit exceeded")] ReferenceLimit, + #[error("incalid proof")] + InvalidProof(&'static str), } impl From for Error { @@ -274,6 +277,27 @@ impl GroveDb { Ok(proof_result) } + pub fn verify_proof(path: &[&[u8]], proofs: Vec>) -> Result { + // Should proof verification require the expected root hash?? I believe so. + + // Make sure that the length of the path is the same as the length of the proofs + // What kind of error should I return + + // Need to have more than one proof + // if proofs.len() < 2 { + // // Can create an invalid proof type + // Err("Not enough proofs"); + // } + // + // let resulter = execute_proof(proofs[0].as_bytes()).unwrap(); + + let compressed_path = Self::compress_path(path, None); + + + todo!() + + } + /// Method to propagate updated subtree root hashes up to GroveDB root fn propagate_changes(&mut self, path: &[&[u8]]) -> Result<(), Error> { let mut split_path = path.split_last(); diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 9d6351d10..e780b995b 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -289,3 +289,28 @@ fn test_proof_construction() { assert_eq!(proof[2], root_tree.proof(&vec![0]).to_bytes()); } + +#[test] +fn test_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"key1".to_vec(), + Element::Item(b"value1".to_vec()), + ) + .expect("successful item insert"); + + dbg!(temp_db.root_tree.root().unwrap()); + let proof = temp_db.proof(&[TEST_LEAF, b"innertree"], QueryItem::Key(b"key1".to_vec())).unwrap(); + + let result_map = GroveDb::verify_proof(&[TEST_LEAF, b"innertree"], proof).unwrap(); + + assert_eq!( + result_map.get(b"key1").unwrap().unwrap(), + b"value1" + ); +} From d9700029a631ca03bcf535c435db277e8a778959 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 8 Dec 2021 05:38:25 +0100 Subject: [PATCH 02/20] Results correct result map --- grovedb/src/lib.rs | 10 +++++++++- grovedb/src/tests.rs | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 960245729..cdd8aa3e5 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -14,6 +14,7 @@ use merk::{self, proofs::Query, rocksdb, Merk, execute_proof}; use rs_merkle::{algorithms::Sha256, MerkleTree}; use merk::proofs::query::Map; use subtree::Element; +use crate::Error::InvalidProof; /// Limit of possible indirections const MAX_REFERENCE_HOPS: usize = 10; @@ -293,9 +294,16 @@ impl GroveDb { let compressed_path = Self::compress_path(path, None); + // Should it really be 2 or more?? + if proofs.len() < 2 { + return Err(Error::InvalidProof("Proof length should be 2 or more")); + } - todo!() + // I need to track result and subsequent root hashes + // We return the leaf result map + let (last_root_hash, leaf_result_map) = execute_proof(&proofs[0][..]).unwrap(); + Ok(leaf_result_map) } /// Method to propagate updated subtree root hashes up to GroveDB root diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index e780b995b..4696e18bb 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -308,9 +308,10 @@ fn test_proof_verification() { let proof = temp_db.proof(&[TEST_LEAF, b"innertree"], QueryItem::Key(b"key1".to_vec())).unwrap(); let result_map = GroveDb::verify_proof(&[TEST_LEAF, b"innertree"], proof).unwrap(); + let elem: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); assert_eq!( - result_map.get(b"key1").unwrap().unwrap(), - b"value1" + elem, + Element::Item(b"value1".to_vec()) ); } From c62c4d72376cc3f0a6247c434afc03818f8b15f5 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 9 Dec 2021 08:48:14 +0100 Subject: [PATCH 03/20] Verifying root hash --- grovedb/src/lib.rs | 51 ++++++++++++++++++++++++++------------------ grovedb/src/tests.rs | 16 ++++++++------ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index cdd8aa3e5..5341f4034 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -10,10 +10,14 @@ use std::{ }; pub use merk::proofs::query::QueryItem; -use merk::{self, proofs::Query, rocksdb, Merk, execute_proof}; -use rs_merkle::{algorithms::Sha256, MerkleTree}; -use merk::proofs::query::Map; +use merk::{ + self, execute_proof, + proofs::{query::Map, Query}, + rocksdb, Merk, +}; +use rs_merkle::{algorithms::Sha256, MerkleProof, MerkleTree}; use subtree::Element; + use crate::Error::InvalidProof; /// Limit of possible indirections @@ -251,6 +255,7 @@ impl GroveDb { .root_leaf_keys .get(*key) .ok_or(Error::InvalidPath("root key not found"))?; + println!("Key index {}", root_key_index); proofs.push(self.root_tree.proof(&[*root_key_index]).to_bytes()); } else { proofs.push(self.prove_item(path_slice, QueryItem::Key(key.to_vec()))?); @@ -278,20 +283,11 @@ impl GroveDb { Ok(proof_result) } - pub fn verify_proof(path: &[&[u8]], proofs: Vec>) -> Result { - // Should proof verification require the expected root hash?? I believe so. - - // Make sure that the length of the path is the same as the length of the proofs - // What kind of error should I return - - // Need to have more than one proof - // if proofs.len() < 2 { - // // Can create an invalid proof type - // Err("Not enough proofs"); - // } - // - // let resulter = execute_proof(proofs[0].as_bytes()).unwrap(); - + pub fn verify_proof( + path: &[&[u8]], + proofs: Vec>, + expected_root_hash: [u8; 32], + ) -> Result { let compressed_path = Self::compress_path(path, None); // Should it really be 2 or more?? @@ -299,11 +295,24 @@ impl GroveDb { return Err(Error::InvalidProof("Proof length should be 2 or more")); } - // I need to track result and subsequent root hashes - // We return the leaf result map - let (last_root_hash, leaf_result_map) = execute_proof(&proofs[0][..]).unwrap(); + let (mut last_root_hash, mut last_result_map) = execute_proof(&proofs[0][..]).unwrap(); + println!("Last root hash first {:?}", last_root_hash); + + for i in 1..proofs.len() - 1 { + last_root_hash = execute_proof(&proofs[i][..]).unwrap().0; + println!("Last root hash loop {:?}", last_root_hash); + } - Ok(leaf_result_map) + // Root tree proof + // Need to know the indices and how many leaves are in the root! + let root_proof = MerkleProof::::try_from(&proofs[proofs.len() - 1][..]).unwrap(); + println!("Last root hash {:?}", last_root_hash); + let a: [u8; 32] = last_root_hash; + if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { + Ok(last_result_map) + } else { + return Err(Error::InvalidProof("Root hashes didn't match")); + } } /// Method to propagate updated subtree root hashes up to GroveDB root diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 4696e18bb..25a3fbf14 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -305,13 +305,17 @@ fn test_proof_verification() { .expect("successful item insert"); dbg!(temp_db.root_tree.root().unwrap()); - let proof = temp_db.proof(&[TEST_LEAF, b"innertree"], QueryItem::Key(b"key1".to_vec())).unwrap(); + let proof = temp_db + .proof(&[TEST_LEAF, b"innertree"], QueryItem::Key(b"key1".to_vec())) + .unwrap(); - let result_map = GroveDb::verify_proof(&[TEST_LEAF, b"innertree"], proof).unwrap(); + let result_map = GroveDb::verify_proof( + &[TEST_LEAF, b"innertree"], + proof, + temp_db.root_tree.root().unwrap(), + ) + .unwrap(); let elem: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); - assert_eq!( - elem, - Element::Item(b"value1".to_vec()) - ); + assert_eq!(elem, Element::Item(b"value1".to_vec())); } From fb84e862ca05794314ab2a39d315c33b620ccb8d Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 9 Dec 2021 10:15:15 +0100 Subject: [PATCH 04/20] Verifying proof path --- grovedb/src/lib.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 5341f4034..0ed059b59 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -288,31 +288,38 @@ impl GroveDb { proofs: Vec>, expected_root_hash: [u8; 32], ) -> Result { - let compressed_path = Self::compress_path(path, None); - // Should it really be 2 or more?? if proofs.len() < 2 { return Err(Error::InvalidProof("Proof length should be 2 or more")); } let (mut last_root_hash, mut last_result_map) = execute_proof(&proofs[0][..]).unwrap(); - println!("Last root hash first {:?}", last_root_hash); - for i in 1..proofs.len() - 1 { - last_root_hash = execute_proof(&proofs[i][..]).unwrap().0; - println!("Last root hash loop {:?}", last_root_hash); + for i in 1..proofs.len() { + if i == proofs.len() - 1 { + // Prove the root + let root_proof = MerkleProof::::try_from(&proofs[i][..]).unwrap(); + let a: [u8; 32] = last_root_hash; + if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { + break; + } else { + return Err(Error::InvalidProof("Root hashes didn't match")); + } + } else { + let proof_result = execute_proof(&proofs[i][..]).unwrap(); + last_root_hash = proof_result.0; + let result_map = proof_result.1; + + // Error if proof does not include the data and no absence proof + // None if absence proof + // We want must no error and not absent + result_map + .get(path[i])? + .ok_or(Error::InvalidProof("Bad path")); + } } - // Root tree proof - // Need to know the indices and how many leaves are in the root! - let root_proof = MerkleProof::::try_from(&proofs[proofs.len() - 1][..]).unwrap(); - println!("Last root hash {:?}", last_root_hash); - let a: [u8; 32] = last_root_hash; - if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { - Ok(last_result_map) - } else { - return Err(Error::InvalidProof("Root hashes didn't match")); - } + Ok(last_result_map) } /// Method to propagate updated subtree root hashes up to GroveDB root From 9fd9a83c35aceef72cc258d57a348c6564fb3929 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 9 Dec 2021 10:24:59 +0100 Subject: [PATCH 05/20] Added better comments + more constraints --- grovedb/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 0ed059b59..c51c6014c 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -293,11 +293,15 @@ impl GroveDb { return Err(Error::InvalidProof("Proof length should be 2 or more")); } + if proofs.len() - 1 != path.len() { + return Err(Error::InvalidProof("Proof length should be one greater than path")); + } + let (mut last_root_hash, mut last_result_map) = execute_proof(&proofs[0][..]).unwrap(); for i in 1..proofs.len() { if i == proofs.len() - 1 { - // Prove the root + // Last proof (root proof) let root_proof = MerkleProof::::try_from(&proofs[i][..]).unwrap(); let a: [u8; 32] = last_root_hash; if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { @@ -306,13 +310,13 @@ impl GroveDb { return Err(Error::InvalidProof("Root hashes didn't match")); } } else { + // Merk proof, validate that the proof is valid and + // the result map contains the last hash i.e the previous + // merk was a child of this merk let proof_result = execute_proof(&proofs[i][..]).unwrap(); last_root_hash = proof_result.0; let result_map = proof_result.1; - // Error if proof does not include the data and no absence proof - // None if absence proof - // We want must no error and not absent result_map .get(path[i])? .ok_or(Error::InvalidProof("Bad path")); From 592e8ad8665d26af870b152b11af55b5e4fc4f93 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 13 Dec 2021 10:23:50 +0100 Subject: [PATCH 06/20] Basic corrections --- grovedb/src/lib.rs | 13 +++++-------- grovedb/src/tests.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index c51c6014c..4b7e3f36a 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -18,8 +18,6 @@ use merk::{ use rs_merkle::{algorithms::Sha256, MerkleProof, MerkleTree}; use subtree::Element; -use crate::Error::InvalidProof; - /// Limit of possible indirections const MAX_REFERENCE_HOPS: usize = 10; /// A key to store serialized data about subtree prefixes to restore HADS @@ -42,7 +40,7 @@ pub enum Error { CyclicReference, #[error("reference hops limit exceeded")] ReferenceLimit, - #[error("incalid proof")] + #[error("invalid proof")] InvalidProof(&'static str), } @@ -285,10 +283,9 @@ impl GroveDb { pub fn verify_proof( path: &[&[u8]], - proofs: Vec>, + proofs: &Vec>, // Generic into_iterator (trait) u8 expected_root_hash: [u8; 32], ) -> Result { - // Should it really be 2 or more?? if proofs.len() < 2 { return Err(Error::InvalidProof("Proof length should be 2 or more")); } @@ -297,7 +294,7 @@ impl GroveDb { return Err(Error::InvalidProof("Proof length should be one greater than path")); } - let (mut last_root_hash, mut last_result_map) = execute_proof(&proofs[0][..]).unwrap(); + let (mut last_root_hash, leaf_result_map) = execute_proof(&proofs[0][..]).unwrap(); for i in 1..proofs.len() { if i == proofs.len() - 1 { @@ -311,7 +308,7 @@ impl GroveDb { } } else { // Merk proof, validate that the proof is valid and - // the result map contains the last hash i.e the previous + // the result map contains the last root hash i.e the previous // merk was a child of this merk let proof_result = execute_proof(&proofs[i][..]).unwrap(); last_root_hash = proof_result.0; @@ -323,7 +320,7 @@ impl GroveDb { } } - Ok(last_result_map) + Ok(leaf_result_map) } /// Method to propagate updated subtree root hashes up to GroveDB root diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 25a3fbf14..eedebc360 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -296,6 +296,9 @@ fn test_proof_verification() { temp_db .insert(&[TEST_LEAF], b"innertree".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"], @@ -303,6 +306,13 @@ fn test_proof_verification() { 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 proof = temp_db @@ -311,7 +321,7 @@ fn test_proof_verification() { let result_map = GroveDb::verify_proof( &[TEST_LEAF, b"innertree"], - proof, + &proof, temp_db.root_tree.root().unwrap(), ) .unwrap(); From 7ca601a1c3cbc7a623b3d2269bef5a03a6bdcb33 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 13 Dec 2021 14:49:12 +0100 Subject: [PATCH 07/20] Added failing test for invalid path --- grovedb/src/lib.rs | 4 ++- grovedb/src/tests.rs | 61 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 4b7e3f36a..e8e68b7f6 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -291,7 +291,9 @@ impl GroveDb { } if proofs.len() - 1 != path.len() { - return Err(Error::InvalidProof("Proof length should be one greater than path")); + return Err(Error::InvalidProof( + "Proof length should be one greater than path", + )); } let (mut last_root_hash, leaf_result_map) = execute_proof(&proofs[0][..]).unwrap(); diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index eedebc360..d65aaaaf5 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -291,7 +291,7 @@ fn test_proof_construction() { } #[test] -fn test_proof_verification() { +fn test_successful_proof_verification() { let mut temp_db = make_grovedb(); temp_db .insert(&[TEST_LEAF], b"innertree".to_vec(), Element::empty_tree()) @@ -329,3 +329,62 @@ fn test_proof_verification() { 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 some random root, 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"key1".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"key1".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 result_map = GroveDb::verify_proof( + &[TEST_LEAF, b"innertree", b"innertree-2"], + &proofs, + root_tree.root().unwrap(), + ) + .unwrap(); + let elem: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); + + assert_eq!(elem, Element::Item(b"value1".to_vec())); +} From 7786c9edf1625e89f0a0313ed83dae7e37f55d2d Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 13 Dec 2021 15:14:16 +0100 Subject: [PATCH 08/20] Implemented proof path validation + failing test passes --- grovedb/src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index e8e68b7f6..cab451da4 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -313,12 +313,23 @@ impl GroveDb { // the result map contains the last root hash i.e the previous // merk was a child of this merk let proof_result = execute_proof(&proofs[i][..]).unwrap(); - last_root_hash = proof_result.0; let result_map = proof_result.1; - result_map - .get(path[i])? - .ok_or(Error::InvalidProof("Bad path")); + // let elem: Element = bincode::deserialize(result_map + // .get(path[i])? + // .ok_or(Error::InvalidProof("Bad path"))kkk); + let elem: Element = + bincode::deserialize(result_map.get(path[i]).unwrap().unwrap()).unwrap(); + let merk_root_hash = match elem { + Element::Tree(hash) => hash, + _ => panic!("Intermidiate proofs should be for trees"), + }; + + if merk_root_hash != last_root_hash { + return Err(Error::InvalidProof("Bad path")); + } + + last_root_hash = proof_result.0; } } From 92b99c9a8aaac8f79d4aaa651175ea6696e00908 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 13 Dec 2021 15:23:39 +0100 Subject: [PATCH 09/20] fmt --- grovedb/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index d65aaaaf5..6c1525bdf 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -339,7 +339,7 @@ fn test_malicious_proof_verification() { // construct a malicious proof // 4 trees, merk_one, merk_two, merk_three, root - // root references m3, m3 references some random root, m2 references m1 + // 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(); @@ -359,7 +359,7 @@ fn test_malicious_proof_verification() { merk_two_element.insert(&mut merk_two, b"innertree-2".to_vec()); let mut proof_query = Query::new(); - proof_query.insert_key(b"key1".to_vec()); + proof_query.insert_key(b"innertree-2".to_vec()); proofs.push(merk_two.prove(proof_query).unwrap()); // Merk Three @@ -368,7 +368,7 @@ fn test_malicious_proof_verification() { merk_three_element.insert(&mut merk_three, b"innertree".to_vec()); let mut proof_query = Query::new(); - proof_query.insert_key(b"key1".to_vec()); + proof_query.insert_key(b"innertree".to_vec()); proofs.push(merk_three.prove(proof_query).unwrap()); let another_test_leaf_merk = TempMerk::new(); From 239538cd6cd0b7e2cf88ebf68534a94db3c56240 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 16 Dec 2021 09:17:13 +0100 Subject: [PATCH 10/20] Changed indexing logic to iteration --- grovedb/src/lib.rs | 40 +++++++++++++++++++++++----------------- grovedb/src/tests.rs | 19 ++++++++++++++++--- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index cab451da4..18192e710 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -296,30 +296,27 @@ impl GroveDb { )); } - let (mut last_root_hash, leaf_result_map) = execute_proof(&proofs[0][..]).unwrap(); + let mut proof_iterator = proofs.iter(); + let reverse_path_iterator = path.iter().rev(); - for i in 1..proofs.len() { - if i == proofs.len() - 1 { - // Last proof (root proof) - let root_proof = MerkleProof::::try_from(&proofs[i][..]).unwrap(); - let a: [u8; 32] = last_root_hash; - if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { - break; - } else { - return Err(Error::InvalidProof("Root hashes didn't match")); - } - } else { + let leaf_proof = proof_iterator + .next() + .expect("Constraint checks above enforces leaf proof must exist"); + + let (mut last_root_hash, leaf_result_map) = execute_proof(&leaf_proof[..])?; + + let mut proof_path_zip = proof_iterator.zip(path.iter().rev()).peekable(); + + while let Some((proof, key)) = proof_path_zip.next() { + if proof_path_zip.peek().is_some() { // Merk proof, validate that the proof is valid and // the result map contains the last root hash i.e the previous // merk was a child of this merk - let proof_result = execute_proof(&proofs[i][..]).unwrap(); + let proof_result = execute_proof(&proof[..]).unwrap(); let result_map = proof_result.1; - // let elem: Element = bincode::deserialize(result_map - // .get(path[i])? - // .ok_or(Error::InvalidProof("Bad path"))kkk); let elem: Element = - bincode::deserialize(result_map.get(path[i]).unwrap().unwrap()).unwrap(); + bincode::deserialize(result_map.get(key).unwrap().unwrap()).unwrap(); let merk_root_hash = match elem { Element::Tree(hash) => hash, _ => panic!("Intermidiate proofs should be for trees"), @@ -330,6 +327,15 @@ impl GroveDb { } last_root_hash = proof_result.0; + } else { + // Last proof (root proof) + let root_proof = MerkleProof::::try_from(&proof[..]).unwrap(); + let a: [u8; 32] = last_root_hash; + if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { + break; + } else { + return Err(Error::InvalidProof("Root hashes didn't match")); + } } } diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 6c1525bdf..ca2fd0df0 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -296,16 +296,26 @@ fn test_successful_proof_verification() { 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"], + &[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"], @@ -316,11 +326,14 @@ fn test_successful_proof_verification() { dbg!(temp_db.root_tree.root().unwrap()); let proof = temp_db - .proof(&[TEST_LEAF, b"innertree"], QueryItem::Key(b"key1".to_vec())) + .proof( + &[TEST_LEAF, b"innertree", b"innertree1.1"], + QueryItem::Key(b"key1".to_vec()), + ) .unwrap(); let result_map = GroveDb::verify_proof( - &[TEST_LEAF, b"innertree"], + &[TEST_LEAF, b"innertree", b"innertree1.1"], &proof, temp_db.root_tree.root().unwrap(), ) From 193fe28deb6ed2ffbcb340884588d7e3fa73b53f Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 16 Dec 2021 15:58:03 +0100 Subject: [PATCH 11/20] fixed unused variable --- grovedb/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 18192e710..997587dc9 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -305,14 +305,14 @@ impl GroveDb { let (mut last_root_hash, leaf_result_map) = execute_proof(&leaf_proof[..])?; - let mut proof_path_zip = proof_iterator.zip(path.iter().rev()).peekable(); + let mut proof_path_zip = proof_iterator.zip(reverse_path_iterator).peekable(); while let Some((proof, key)) = proof_path_zip.next() { if proof_path_zip.peek().is_some() { // Merk proof, validate that the proof is valid and // the result map contains the last root hash i.e the previous // merk was a child of this merk - let proof_result = execute_proof(&proof[..]).unwrap(); + let proof_result = execute_proof(&proof[..])?; let result_map = proof_result.1; let elem: Element = From d7fdb58dc2561cf1a9d8131f6bfb0ee5278850cc Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 16 Dec 2021 16:43:43 +0100 Subject: [PATCH 12/20] Proof verification only depends on proof byte data --- grovedb/src/lib.rs | 16 ++++++++++++---- grovedb/src/tests.rs | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index f9b706023..39b8c10b0 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -287,19 +287,22 @@ impl GroveDb { pub fn verify_proof( path: &[&[u8]], - proofs: &Vec>, // Generic into_iterator (trait) u8 + proofs: &mut Vec>, // Generic into_iterator (trait) u8 expected_root_hash: [u8; 32], ) -> Result { if proofs.len() < 2 { return Err(Error::InvalidProof("Proof length should be 2 or more")); } - if proofs.len() - 1 != path.len() { + if proofs.len() - 2 != path.len() { return Err(Error::InvalidProof( - "Proof length should be one greater than path", + "Proof length should be two greater than path", )); } + let root_leaf_keys: HashMap, usize> = + bincode::deserialize(&proofs.pop().unwrap()[..])?; + let mut proof_iterator = proofs.iter(); let reverse_path_iterator = path.iter().rev(); @@ -335,7 +338,12 @@ impl GroveDb { // Last proof (root proof) let root_proof = MerkleProof::::try_from(&proof[..]).unwrap(); let a: [u8; 32] = last_root_hash; - if root_proof.verify(expected_root_hash, &vec![0], &[a], 2) { + if root_proof.verify( + expected_root_hash, + &vec![root_leaf_keys[&key.to_vec()]], + &[a], + root_leaf_keys.len(), + ) { break; } else { return Err(Error::InvalidProof("Root hashes didn't match")); diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index 54d1c38a5..6ed85b21a 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -331,7 +331,7 @@ fn test_successful_proof_verification() { .expect("successful item insert"); dbg!(temp_db.root_tree.root().unwrap()); - let proof = temp_db + let mut proof = temp_db .proof( &[TEST_LEAF, b"innertree", b"innertree1.1"], QueryItem::Key(b"key1".to_vec()), @@ -340,7 +340,7 @@ fn test_successful_proof_verification() { let result_map = GroveDb::verify_proof( &[TEST_LEAF, b"innertree", b"innertree1.1"], - &proof, + &mut proof, temp_db.root_tree.root().unwrap(), ) .unwrap(); @@ -399,7 +399,7 @@ fn test_malicious_proof_verification() { let result_map = GroveDb::verify_proof( &[TEST_LEAF, b"innertree", b"innertree-2"], - &proofs, + &mut proofs, root_tree.root().unwrap(), ) .unwrap(); From 93ef00cdba55fab4a4c63b28b60113ec0eb9f642 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 16 Dec 2021 16:54:53 +0100 Subject: [PATCH 13/20] Fixed comments --- grovedb/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 4d0e1cbd5..a5a337fcd 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -316,9 +316,9 @@ impl GroveDb { while let Some((proof, key)) = proof_path_zip.next() { if proof_path_zip.peek().is_some() { - // Merk proof, validate that the proof is valid and - // the result map contains the last root hash i.e the previous - // merk was a child of this merk + // 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 = execute_proof(&proof[..])?; let result_map = proof_result.1; From 77d323d723c6cd15cc5ca77122a2b06545b26a10 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 17 Dec 2021 07:58:01 +0100 Subject: [PATCH 14/20] Better error handling --- grovedb/src/lib.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index a5a337fcd..d7a60bd80 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -18,6 +18,8 @@ use merk::{ use rs_merkle::{algorithms::Sha256, MerkleProof, MerkleTree}; use subtree::Element; +use crate::Error::InvalidProof; + /// Limit of possible indirections const MAX_REFERENCE_HOPS: usize = 10; /// A key to store serialized data about subtree prefixes to restore HADS @@ -261,7 +263,8 @@ impl GroveDb { split_path = path_slice.split_last(); } - // Append the root leaf keys hash map to proof to provide context when verifying proof + // Append the root leaf keys hash map to proof to provide context when verifying + // proof let aux_data = bincode::serialize(&self.root_leaf_keys)?; proofs.push(aux_data); @@ -310,7 +313,10 @@ impl GroveDb { .next() .expect("Constraint checks above enforces leaf proof must exist"); - let (mut last_root_hash, leaf_result_map) = execute_proof(&leaf_proof[..])?; + let (mut last_root_hash, leaf_result_map) = match execute_proof(&leaf_proof[..]) { + Ok(result) => Ok(result), + Err(e) => Err(Error::InvalidProof("Invalid proof element")), + }?; let mut proof_path_zip = proof_iterator.zip(reverse_path_iterator).peekable(); @@ -319,15 +325,20 @@ impl GroveDb { // 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 = execute_proof(&proof[..])?; + let proof_result = match execute_proof(&proof[..]) { + Ok(result) => Ok(result), + Err(e) => 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) => hash, - _ => panic!("Intermidiate proofs should be for trees"), - }; + 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")); @@ -336,7 +347,10 @@ impl GroveDb { last_root_hash = proof_result.0; } else { // Last proof (root proof) - let root_proof = MerkleProof::::try_from(&proof[..]).unwrap(); + let root_proof = match MerkleProof::::try_from(&proof[..]) { + Ok(root_proof) => Ok(root_proof), + Err(e) => Err(Error::InvalidProof("Invalid proof element")), + }?; let a: [u8; 32] = last_root_hash; if root_proof.verify( expected_root_hash, From 457bb7d347fb6f7f62c0391503d76bf91015963a Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu <40731160+iammadab@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:25:35 +0100 Subject: [PATCH 15/20] Update grovedb/src/lib.rs Co-authored-by: Evgeny Fomin --- grovedb/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index d7a60bd80..ec19c5b17 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -255,7 +255,6 @@ impl GroveDb { .root_leaf_keys .get(*key) .ok_or(Error::InvalidPath("root key not found"))?; - println!("Key index {}", root_key_index); proofs.push(self.root_tree.proof(&[*root_key_index]).to_bytes()); } else { proofs.push(self.prove_item(path_slice, QueryItem::Key(key.to_vec()))?); From 6ea0af1827c73eed976e4c153ad5c9620d75b977 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu <40731160+iammadab@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:25:45 +0100 Subject: [PATCH 16/20] Update grovedb/src/lib.rs Co-authored-by: Evgeny Fomin --- grovedb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index ec19c5b17..812551dbc 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -353,7 +353,7 @@ impl GroveDb { let a: [u8; 32] = last_root_hash; if root_proof.verify( expected_root_hash, - &vec![root_leaf_keys[&key.to_vec()]], + &[root_leaf_keys[*key]], &[a], root_leaf_keys.len(), ) { From 801a4f9ecec256055619572832c8c879d1e4b959 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu <40731160+iammadab@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:47:04 +0100 Subject: [PATCH 17/20] Update grovedb/src/lib.rs Co-authored-by: Evgeny Fomin --- grovedb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 812551dbc..e50ff7466 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -42,7 +42,7 @@ pub enum Error { CyclicReference, #[error("reference hops limit exceeded")] ReferenceLimit, - #[error("invalid proof")] + #[error("invalid proof: {0}")] InvalidProof(&'static str), } From 0838a3d4952a2d056bcd59fb9dc7287d51760794 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 20 Dec 2021 08:36:50 +0100 Subject: [PATCH 18/20] Fixed tests --- grovedb/src/tests.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index eb2a4e4ec..bd73e2c35 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -341,11 +341,14 @@ fn test_successful_proof_verification() { ) .expect("successful item insert"); - dbg!(temp_db.root_tree.root().unwrap()); + // 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"], - QueryItem::Key(b"key1".to_vec()), + proof_query, ) .unwrap(); From 785a84647f8bbd3b44533473d6d3fde717a62dc4 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 23 Dec 2021 08:25:21 +0100 Subject: [PATCH 19/20] Returning the root hash instead of verifying --- grovedb/src/lib.rs | 45 ++++++++++++++++++++++---------------------- grovedb/src/tests.rs | 27 +++++++++++++------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index bd0e79486..035b20faf 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; pub use merk::proofs::{query::QueryItem, Query}; -use merk::{self, execute_proof, proofs::query::Map, Merk}; +use merk::{self, proofs::query::Map, Merk}; use rs_merkle::{algorithms::Sha256, MerkleProof, MerkleTree}; use storage::{ rocksdb_storage::{PrefixedRocksDbStorage, PrefixedRocksDbStorageError}, @@ -17,8 +17,6 @@ use storage::{ }; pub use subtree::Element; -use crate::Error::InvalidProof; - /// Limit of possible indirections const MAX_REFERENCE_HOPS: usize = 10; /// A key to store serialized data about subtree prefixes to restore HADS @@ -325,11 +323,10 @@ impl GroveDb { Ok(proof_result) } - pub fn verify_proof( + pub fn execute_proof( path: &[&[u8]], - proofs: &mut Vec>, // Generic into_iterator (trait) u8 - expected_root_hash: [u8; 32], - ) -> Result { + proofs: &mut Vec>, + ) -> Result<([u8; 32], Map), Error> { if proofs.len() < 2 { return Err(Error::InvalidProof("Proof length should be 2 or more")); } @@ -351,21 +348,22 @@ impl GroveDb { .next() .expect("Constraint checks above enforces leaf proof must exist"); - let (mut last_root_hash, leaf_result_map) = match execute_proof(&leaf_proof[..]) { + let (mut last_root_hash, leaf_result_map) = match merk::execute_proof(&leaf_proof[..]) { Ok(result) => Ok(result), - Err(e) => Err(Error::InvalidProof("Invalid proof element")), + 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 execute_proof(&proof[..]) { + let proof_result = match merk::execute_proof(&proof[..]) { Ok(result) => Ok(result), - Err(e) => Err(Error::InvalidProof("Invalid proof element")), + Err(_) => Err(Error::InvalidProof("Invalid proof element")), }?; let result_map = proof_result.1; @@ -387,23 +385,24 @@ impl GroveDb { // Last proof (root proof) let root_proof = match MerkleProof::::try_from(&proof[..]) { Ok(root_proof) => Ok(root_proof), - Err(e) => Err(Error::InvalidProof("Invalid proof element")), + Err(_) => Err(Error::InvalidProof("Invalid proof element")), }?; let a: [u8; 32] = last_root_hash; - if root_proof.verify( - expected_root_hash, - &[root_leaf_keys[*key]], - &[a], - root_leaf_keys.len(), - ) { - break; - } else { - return Err(Error::InvalidProof("Root hashes didn't match")); - } + 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")), + }?, + ); } } - Ok(leaf_result_map) + if let Some(hash) = root_hash { + return Ok((hash, leaf_result_map)); + } else { + return Err(Error::InvalidProof("Invalid proof element")); + } } /// Method to propagate updated subtree root hashes up to GroveDB root diff --git a/grovedb/src/tests.rs b/grovedb/src/tests.rs index c208a8b5f..fcec067ce 100644 --- a/grovedb/src/tests.rs +++ b/grovedb/src/tests.rs @@ -349,14 +349,14 @@ fn test_successful_proof_verification() { .proof(&[TEST_LEAF, b"innertree", b"innertree1.1"], proof_query) .unwrap(); - let result_map = GroveDb::verify_proof( - &[TEST_LEAF, b"innertree", b"innertree1.1"], - &mut proof, - temp_db.root_tree.root().unwrap(), - ) - .unwrap(); - let elem: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).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())); } @@ -408,14 +408,13 @@ fn test_malicious_proof_verification() { let root_tree = MerkleTree::::from_leaves(&leaves); proofs.push(root_tree.proof(&vec![0]).to_bytes()); - let result_map = GroveDb::verify_proof( - &[TEST_LEAF, b"innertree", b"innertree-2"], - &mut proofs, - root_tree.root().unwrap(), - ) - .unwrap(); - let elem: Element = bincode::deserialize(result_map.get(b"key1").unwrap().unwrap()).unwrap(); + 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())); } From 0d08f5fc75e61acbfe2e7d9027bd96a8944bbb55 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 23 Dec 2021 08:35:04 +0100 Subject: [PATCH 20/20] fmt --- grovedb/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/grovedb/src/lib.rs b/grovedb/src/lib.rs index 035b20faf..76249bc15 100644 --- a/grovedb/src/lib.rs +++ b/grovedb/src/lib.rs @@ -323,6 +323,8 @@ 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>, @@ -398,10 +400,10 @@ impl GroveDb { } } - if let Some(hash) = root_hash { - return Ok((hash, leaf_result_map)); + return if let Some(hash) = root_hash { + Ok((hash, leaf_result_map)) } else { - return Err(Error::InvalidProof("Invalid proof element")); + Err(Error::InvalidProof("Invalid proof element")) } }