Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
89bc83d
added invalid proof error type
iammadab Dec 8, 2021
d970002
Results correct result map
iammadab Dec 8, 2021
c62c4d7
Verifying root hash
iammadab Dec 9, 2021
fb84e86
Verifying proof path
iammadab Dec 9, 2021
9fd9a83
Added better comments + more constraints
iammadab Dec 9, 2021
592e8ad
Basic corrections
iammadab Dec 13, 2021
7ca601a
Added failing test for invalid path
iammadab Dec 13, 2021
7786c9e
Implemented proof path validation + failing test passes
iammadab Dec 13, 2021
92b99c9
fmt
iammadab Dec 13, 2021
239538c
Changed indexing logic to iteration
iammadab Dec 16, 2021
193fe28
fixed unused variable
iammadab Dec 16, 2021
e601e32
Merge branch 'feat/proofs' into feat/proof-verification
iammadab Dec 16, 2021
d7fdb58
Proof verification only depends on proof byte data
iammadab Dec 16, 2021
cfac476
Merge branch 'feat/proofs' into feat/proof-verification
iammadab Dec 16, 2021
93ef00c
Fixed comments
iammadab Dec 16, 2021
77d323d
Better error handling
iammadab Dec 17, 2021
457bb7d
Update grovedb/src/lib.rs
iammadab Dec 17, 2021
6ea0af1
Update grovedb/src/lib.rs
iammadab Dec 17, 2021
801a4f9
Update grovedb/src/lib.rs
iammadab Dec 17, 2021
e12586b
Merge branch 'feat/proofs' into feat/proof-verification
iammadab Dec 20, 2021
0838a3d
Fixed tests
iammadab Dec 20, 2021
04c39ee
Merge branch 'feat/proof-verification' of github.com:dashevo/grovedb …
iammadab Dec 20, 2021
5f8deaa
Merged feat/proofs
iammadab Dec 20, 2021
0dd6185
Resolved merge conflicts
iammadab Dec 20, 2021
6762644
Merge branch 'feat/proofs' into feat/proof-verification
iammadab Dec 20, 2021
7e34f19
Merge branch 'master' into feat/proof-verification
iammadab Dec 21, 2021
785a846
Returning the root hash instead of verifying
iammadab Dec 23, 2021
0d08f5f
fmt
iammadab Dec 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions grovedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use std::{
};

pub use merk::proofs::{query::QueryItem, Query};
use merk::{self, Merk};
use rs_merkle::{algorithms::Sha256, MerkleTree};
use merk::{self, proofs::query::Map, Merk};
use rs_merkle::{algorithms::Sha256, MerkleProof, MerkleTree};
use storage::{
rocksdb_storage::{PrefixedRocksDbStorage, PrefixedRocksDbStorageError},
Storage,
Expand All @@ -32,6 +32,8 @@ pub enum Error {
CyclicReference,
#[error("reference hops limit exceeded")]
ReferenceLimit,
#[error("invalid proof: {0}")]
InvalidProof(&'static str),
#[error("invalid path: {0}")]
InvalidPath(&'static str),
// Irrecoverable errors
Expand Down Expand Up @@ -321,6 +323,90 @@ 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<Vec<u8>>,
) -> 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<Vec<u8>, 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() {
Comment thread
iammadab marked this conversation as resolved.
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::<Sha256>::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"))
}
}

/// 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();
Expand Down
110 changes: 110 additions & 0 deletions grovedb/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,116 @@ fn test_proof_construction() {
}

#[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<u8>> = 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::<Sha256>::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()));
}

fn test_checkpoint() {
let mut db = make_grovedb();
let element1 = Element::Item(b"ayy".to_vec());
Expand Down