Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
.idea
Cargo.lock
grove.db
2 changes: 2 additions & 0 deletions grovedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum Error {
InvalidPath(&'static str),
#[error("unable to decode")]
EdError(#[from] ed::Error),
#[error("cyclic reference path")]
CyclicReferencePath,
}

impl From<merk::Error> for Error {
Expand Down
55 changes: 46 additions & 9 deletions grovedb/src/subtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,32 @@ impl Element {

/// Recursively follow `Element::Reference`
fn follow_reference(self, merk: &Merk) -> Result<Element, Error> {
if let Element::Reference(reference_merk_key) = self {
let element = Element::decode(
merk.get(reference_merk_key.as_slice())?
.ok_or(Error::InvalidPath("key not found in Merk"))?
.as_slice(),
)?;
element.follow_reference(merk)
} else {
Ok(self)
fn follow_reference_with_path(
element: Element,
merk: &Merk,
paths: &mut Vec<Vec<u8>>,
) -> Result<Element, Error> {
if let Element::Reference(reference_merk_key) = element {
// Check if the reference merk key has been visited before
// if it has then we have a cycle <return an error>
if paths.contains(&reference_merk_key) {
return Err(Error::CyclicReferencePath);
}
let element = Element::decode(
merk.get(reference_merk_key.as_slice())?
.ok_or(Error::InvalidPath("key not found in Merk"))?
.as_slice(),
)?;

paths.push(reference_merk_key);
follow_reference_with_path(element, merk, paths)
} else {
Ok(element)
}
}

let mut reference_paths: Vec<Vec<u8>> = Vec::new();
follow_reference_with_path(self, merk, &mut reference_paths)
}

/// A helper method to build Merk keys (and RocksDB as well) out of path +
Expand Down Expand Up @@ -72,6 +88,7 @@ impl Element {
.ok_or(Error::InvalidPath("key not found in Merk"))?
.as_slice(),
)?;

element.follow_reference(&merk)
}

Expand Down Expand Up @@ -141,4 +158,24 @@ mod tests {
Element::Item(b"value".to_vec()),
);
}

#[test]
fn test_circular_references() {
let tmp_dir = TempDir::new("db").unwrap();
let mut merk = Merk::open(tmp_dir.path()).unwrap();

Element::Tree
.insert(&mut merk, &[], b"tree-key")
.expect("expected successful insertion");

// r1 points to r2 and r2 points to r1 (cycle!)
Element::new_reference(&[b"tree-key"], b"reference-2")
.insert(&mut merk, &[b"tree-key"], b"reference-1")
.expect("expected successful reference insertion");
Element::new_reference(&[b"tree-key"], b"reference-1")
.insert(&mut merk, &[b"tree-key"], b"reference-2")
.expect("expected successful reference insertion");

assert!(Element::get(&merk, &[b"tree-key"], b"reference-1").is_err());
}
}