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
73 changes: 67 additions & 6 deletions grovedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@ 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},
rocksdb_storage::{self, PrefixedRocksDbStorage, PrefixedRocksDbStorageError},
Storage,
};
pub use subtree::Element;
Expand Down Expand Up @@ -152,6 +148,71 @@ impl GroveDb {
res
}

pub fn delete(&mut self, path: &[&[u8]], key: Vec<u8>) -> Result<(), Error> {
let element = self.get_raw(path, &key)?;
if path.is_empty() {
// Attempt to delete a root tree leaf
Err(Error::InvalidPath(
"root tree leafs currently cannot be deleted",
))
} else {
let mut merk = self
.subtrees
.get_mut(&Self::compress_subtree_key(path, None))
.ok_or(Error::InvalidPath("no subtree found under that path"))?;
Element::delete(&mut merk, key.clone())?;
if let Element::Tree(_) = element {
// TODO: dumb traversal should not be tolerated
let mut concat_path: Vec<Vec<u8>> = path.iter().map(|x| x.to_vec()).collect();
concat_path.push(key);
let subtrees_paths = self.find_subtrees(concat_path)?;
for subtree_path in subtrees_paths {
// TODO: eventually we need to do something about this nested slices
let subtree_path_ref: Vec<&[u8]> =
subtree_path.iter().map(|x| x.as_slice()).collect();
let prefix = Self::compress_subtree_key(&subtree_path_ref, None);
if let Some(subtree) = self.subtrees.remove(&prefix) {
subtree.clear().map_err(|e| {
Error::CorruptedData(format!(
"unable to cleanup tree from storage: {}",
e
))
})?;
}
}
}
self.propagate_changes(path)?;
Ok(())
}
}

// TODO: dumb traversal should not be tolerated
/// Finds keys which are trees for a given subtree recursively.
/// One element means a key of a `merk`, n > 1 elements mean relative path
/// for a deeply nested subtree.
fn find_subtrees(&self, path: Vec<Vec<u8>>) -> Result<Vec<Vec<Vec<u8>>>, Error> {
let mut queue: Vec<Vec<Vec<u8>>> = vec![path.clone()];
let mut result: Vec<Vec<Vec<u8>>> = vec![path.clone()];

while let Some(q) = queue.pop() {
// TODO: eventually we need to do something about this nested slices
let q_ref: Vec<&[u8]> = q.iter().map(|x| x.as_slice()).collect();
let mut iter = self.elements_iterator(&q_ref)?;
while let Some((key, value)) = iter.next()? {
match value {
Element::Tree(_) => {
let mut sub_path = q.clone();
sub_path.push(key);
queue.push(sub_path.clone());
result.push(sub_path);
}
_ => {}
}
}
}
Ok(result)
}

// TODO: split the function into smaller ones
pub fn insert(
&mut self,
Expand Down
8 changes: 8 additions & 0 deletions grovedb/src/subtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ impl Element {
Element::Tree(Default::default())
}

/// Delete an element from Merk under a key
pub fn delete(merk: &mut Merk<PrefixedRocksDbStorage>, key: Vec<u8>) -> Result<(), Error> {
// TODO: delete references on this element
let batch = [(key, Op::Delete)];
merk.apply(&batch, &[])
.map_err(|e| Error::CorruptedData(e.to_string()))
}

/// Get an element from Merk under a key; path should be resolved and proper
/// Merk should be loaded by this moment
pub fn get(merk: &Merk<PrefixedRocksDbStorage>, key: &[u8]) -> Result<Element, Error> {
Expand Down
86 changes: 86 additions & 0 deletions grovedb/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,89 @@ fn test_compress_path_not_possible_collision() {
GroveDb::compress_subtree_key(&path_a, None),
);
}

#[test]
fn test_element_deletion() {
let mut db = make_grovedb();
let element = Element::Item(b"ayy".to_vec());
db.insert(&[TEST_LEAF], b"key".to_vec(), element.clone())
.expect("successful insert");
let root_hash = db.root_tree.root().unwrap();
assert!(db.delete(&[TEST_LEAF], b"key".to_vec()).is_ok(),);
assert!(matches!(
db.get(&[TEST_LEAF], b"key"),
Err(Error::InvalidPath(_))
));
assert_ne!(root_hash, db.root_tree.root().unwrap());
}

#[test]
fn test_find_subtrees() {
let element = Element::Item(b"ayy".to_vec());
let mut db = make_grovedb();
// Insert some nested subtrees
db.insert(&[TEST_LEAF], b"key1".to_vec(), Element::empty_tree())
.expect("successful subtree 1 insert");
db.insert(
&[TEST_LEAF, b"key1"],
b"key2".to_vec(),
Element::empty_tree(),
)
.expect("successful subtree 2 insert");
// Insert an element into subtree
db.insert(
&[TEST_LEAF, b"key1", b"key2"],
b"key3".to_vec(),
element.clone(),
)
.expect("successful value insert");
db.insert(&[TEST_LEAF], b"key4".to_vec(), Element::empty_tree())
.expect("successful subtree 3 insert");
let subtrees = db
.find_subtrees(vec![TEST_LEAF.to_vec()])
.expect("cannot get subtrees");
assert_eq!(
vec![
vec![TEST_LEAF.to_vec()],
vec![TEST_LEAF.to_vec(), b"key1".to_vec()],
vec![TEST_LEAF.to_vec(), b"key4".to_vec()],
vec![TEST_LEAF.to_vec(), b"key1".to_vec(), b"key2".to_vec()],
],
subtrees
);
}

#[test]
fn test_subtree_deletion() {
let element = Element::Item(b"ayy".to_vec());
let mut db = make_grovedb();
// Insert some nested subtrees
db.insert(&[TEST_LEAF], b"key1".to_vec(), Element::empty_tree())
.expect("successful subtree 1 insert");
db.insert(
&[TEST_LEAF, b"key1"],
b"key2".to_vec(),
Element::empty_tree(),
)
.expect("successful subtree 2 insert");
// Insert an element into subtree
db.insert(
&[TEST_LEAF, b"key1", b"key2"],
b"key3".to_vec(),
element.clone(),
)
.expect("successful value insert");
db.insert(&[TEST_LEAF], b"key4".to_vec(), Element::empty_tree())
.expect("successful subtree 3 insert");

let root_hash = db.root_tree.root().unwrap();
db.delete(&[TEST_LEAF], b"key1".to_vec())
.expect("unable to delete subtree");
assert!(matches!(
db.get(&[TEST_LEAF, b"key1", b"key2"], b"key3"),
Err(Error::InvalidPath(_))
));
assert_eq!(db.subtrees.len(), 3); // TEST_LEAF, ANOTHER_TEST_LEAF and TEST_LEAF.key4 stay
assert!(db.get(&[TEST_LEAF], b"key4").is_ok());
assert_ne!(root_hash, db.root_tree.root().unwrap());
}
17 changes: 16 additions & 1 deletion merk/src/merk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod chunks;
use std::{cell::Cell, cmp::Ordering, collections::LinkedList};

use anyhow::{anyhow, bail, Result};
use storage::{self, Batch, Storage, Store};
use storage::{self, Batch, RawIterator, Storage, Store};

use crate::{
proofs::{encode_into, query::QueryItem, Query},
Expand Down Expand Up @@ -40,6 +40,21 @@ where
Ok(merk)
}

/// Deletes tree data
pub fn clear(self) -> Result<()> {
let mut iter = self.raw_iter();
iter.seek_to_first();
let mut to_delete = self.storage.new_batch()?;
while iter.valid() {
if let Some(key) = iter.key() {
to_delete.delete(key);
}
iter.next();
}
self.storage.commit_batch(to_delete)?;
Ok(())
}

/// Gets an auxiliary value.
pub fn get_aux(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok(self.storage.get_aux(key)?)
Expand Down
2 changes: 2 additions & 0 deletions storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ pub trait RawIterator {

fn next(&mut self);

fn prev(&mut self);

fn value(&self) -> Option<&[u8]>;

fn key(&self) -> Option<&[u8]>;
Expand Down
4 changes: 4 additions & 0 deletions storage/src/rocksdb_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ impl RawIterator for RawPrefixedIterator<'_> {
self.rocksdb_iterator.next();
}

fn prev(&mut self) {
self.rocksdb_iterator.prev();
}

fn value(&self) -> Option<&[u8]> {
if self.valid() {
self.rocksdb_iterator.value()
Expand Down