Skip to content

Commit

Permalink
fix: reference insertion without batch + verify (#327)
Browse files Browse the repository at this point in the history
* add tests

* fix verify function

* reference insertion fixes

* fix ci
  • Loading branch information
fominok authored Aug 14, 2024
1 parent 7c743d5 commit a054032
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 144 deletions.
11 changes: 11 additions & 0 deletions grovedb/src/element/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use grovedb_visualize::visualize_to_vec;
use crate::operations::proof::util::hex_to_ascii;
#[cfg(any(feature = "full", feature = "verify"))]
use crate::reference_path::ReferencePathType;
#[cfg(feature = "full")]
use crate::OperationCost;

#[cfg(any(feature = "full", feature = "verify"))]
/// Optional meta-data to be stored per element
Expand Down Expand Up @@ -153,6 +155,15 @@ impl Element {
Element::SumTree(..) => "sum tree",
}
}

#[cfg(feature = "full")]
pub(crate) fn value_hash(
&self,
grove_version: &grovedb_version::version::GroveVersion,
) -> grovedb_costs::CostResult<grovedb_merk::CryptoHash, crate::Error> {
let bytes = grovedb_costs::cost_return_on_error_default!(self.serialize(grove_version));
crate::value_hash(&bytes).map(Result::Ok)
}
}

#[cfg(any(feature = "full", feature = "visualize"))]
Expand Down
80 changes: 19 additions & 61 deletions grovedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,46 +1063,26 @@ impl GroveDb {
"expected merk to contain value at key".to_string(),
))?;

// Absolute path to the referenced merk and the key in that merk:
let (referenced_path, referenced_key) = {
let referenced_value_hash = {
let mut full_path = path_from_reference_path_type(
reference_path.clone(),
&path.to_vec(),
Some(&key),
)?;
let key = full_path.pop().ok_or_else(|| {
Error::MissingReference(
"can't resolve reference path to absolute path".to_owned(),
let item = self
.follow_reference(
(full_path.as_slice()).into(),
true,
None,
grove_version,
)
})?;
(full_path, key)
.unwrap()?;
item.value_hash(grove_version).unwrap()?
};

// Open another subtree, one that is referenced:
let referenced_merk = self
.open_non_transactional_merk_at_path(
(referenced_path.as_slice()).into(),
None,
grove_version,
)
.unwrap()?;

// Get value hash of the referenced item
let (_, referenced_value_hash) = referenced_merk
.get_value_and_value_hash(
&referenced_key,
true,
None::<&fn(&[u8], &GroveVersion) -> Option<ValueDefinedCostType>>,
grove_version,
)
.unwrap()
.map_err(MerkError)?
.ok_or(Error::CorruptedData(
"expected merk to contain value at key".to_string(),
))?;

// Take the current item (reference) hash and combine it with referenced value's
// hash

let self_actual_value_hash = value_hash(&kv_value).unwrap();
let combined_value_hash =
combine_hash(&self_actual_value_hash, &referenced_value_hash).unwrap();
Expand Down Expand Up @@ -1223,45 +1203,23 @@ impl GroveDb {
"expected merk to contain value at key".to_string(),
))?;

// Absolute path to the referenced merk and the key in that merk:
let (referenced_path, referenced_key) = {
let referenced_value_hash = {
let mut full_path = path_from_reference_path_type(
reference_path.clone(),
&path.to_vec(),
Some(&key),
)?;
let key = full_path.pop().ok_or_else(|| {
Error::MissingReference(
"can't resolve reference path to absolute path".to_owned(),
let item = self
.follow_reference(
(full_path.as_slice()).into(),
true,
Some(transaction),
grove_version,
)
})?;
(full_path, key)
.unwrap()?;
item.value_hash(grove_version).unwrap()?
};

// Open another subtree, one that is referenced:
let referenced_merk = self
.open_transactional_merk_at_path(
(referenced_path.as_slice()).into(),
transaction,
None,
grove_version,
)
.unwrap()?;

// Get value hash of the referenced item
let (_, referenced_value_hash) = referenced_merk
.get_value_and_value_hash(
&referenced_key,
true,
None::<&fn(&[u8], &GroveVersion) -> Option<ValueDefinedCostType>>,
grove_version,
)
.unwrap()
.map_err(MerkError)?
.ok_or(Error::CorruptedData(
"expected merk to contain value at key".to_string(),
))?;

// Take the current item (reference) hash and combine it with referenced value's
// hash
let self_actual_value_hash = value_hash(&kv_value).unwrap();
Expand Down
81 changes: 14 additions & 67 deletions grovedb/src/operations/insert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{collections::HashMap, option::Option::None};
use grovedb_costs::{
cost_return_on_error, cost_return_on_error_no_add, CostResult, CostsExt, OperationCost,
};
use grovedb_merk::tree::value_hash;
#[cfg(feature = "full")]
use grovedb_merk::{tree::NULL_HASH, Merk, MerkOptions};
use grovedb_path::SubtreePath;
Expand Down Expand Up @@ -293,44 +294,18 @@ impl GroveDb {
.wrap_with_cost(OperationCost::default())
);

let (referenced_key, referenced_path) = reference_path.split_last().unwrap();
let subtree_for_reference = cost_return_on_error!(
let referenced_item = cost_return_on_error!(
&mut cost,
self.open_transactional_merk_at_path(
referenced_path.into(),
transaction,
Some(batch),
grove_version,
)
);

let referenced_element_value_hash_opt = cost_return_on_error!(
&mut cost,
Element::get_value_hash(
&subtree_for_reference,
referenced_key,
true,
self.follow_reference(
reference_path.as_slice().into(),
false,
Some(transaction),
grove_version
)
);

let referenced_element_value_hash = cost_return_on_error!(
&mut cost,
referenced_element_value_hash_opt
.ok_or({
let reference_string = reference_path
.iter()
.map(hex::encode)
.collect::<Vec<String>>()
.join("/");
Error::MissingReference(format!(
"reference {}/{} can not be found",
reference_string,
hex::encode(key)
))
})
.wrap_with_cost(OperationCost::default())
);
let referenced_element_value_hash =
cost_return_on_error!(&mut cost, referenced_item.value_hash(grove_version));

cost_return_on_error!(
&mut cost,
Expand Down Expand Up @@ -452,46 +427,18 @@ impl GroveDb {
path_from_reference_path_type(reference_path.clone(), path, Some(key))
.wrap_with_cost(OperationCost::default())
);

// TODO unwrap?
let (referenced_key, referenced_path) = reference_path.split_last().unwrap();
let subtree_for_reference = cost_return_on_error!(
let referenced_item = cost_return_on_error!(
&mut cost,
self.open_non_transactional_merk_at_path(
referenced_path.into(),
Some(batch),
grove_version
)
);

// when there is no transaction, we don't want to use caching
let referenced_element_value_hash_opt = cost_return_on_error!(
&mut cost,
Element::get_value_hash(
&subtree_for_reference,
referenced_key,
self.follow_reference(
reference_path.as_slice().into(),
false,
None,
grove_version
)
);

let referenced_element_value_hash = cost_return_on_error!(
&mut cost,
referenced_element_value_hash_opt
.ok_or({
let reference_string = reference_path
.iter()
.map(hex::encode)
.collect::<Vec<String>>()
.join("/");
Error::MissingReference(format!(
"reference {}/{} can not be found",
reference_string,
hex::encode(key)
))
})
.wrap_with_cost(OperationCost::default())
);
let referenced_element_value_hash =
cost_return_on_error!(&mut cost, referenced_item.value_hash(grove_version));

cost_return_on_error!(
&mut cost,
Expand Down
Loading

0 comments on commit a054032

Please sign in to comment.