Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🧺 use typed errors in public interface #114

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
779b003
manifest: 📦 sort `[dependencies]` table
cratelyn May 3, 2024
18b7c41
manifest: 📦 sort `[dev-dependencies]` table
cratelyn May 3, 2024
cf22b61
tree: 📝 add comment to `JellyfishMerkleTree::get_extreme_key_hash()`
cratelyn May 2, 2024
c2d2001
tree: 🧹 tidy imports
cratelyn Apr 30, 2024
e83d992
restore: 🧹 tidy imports
cratelyn Apr 30, 2024
e0eabaf
node_type: 🧹 tidy imports
cratelyn Apr 30, 2024
ebb4f88
proof: 🍎 use `std::result::Result`
cratelyn Apr 30, 2024
cd81cb3
ics23_impl: 🍎 use `std::result::Result`
cratelyn Apr 30, 2024
e8598d5
tree: 🍎 use `std::result::Result`
cratelyn Apr 30, 2024
bde8008
tree_cache: 🍎 use `std::result::Result`
cratelyn May 1, 2024
b52b08e
restore: 🍎 use `std::result::Result`
cratelyn May 1, 2024
1fb173f
iterator: 🍎 use `std::result::Result`
cratelyn May 3, 2024
a167b37
proof: 🌽 `UpdateMerkleProof::verify_update()` uses `UpdateError`
cratelyn Apr 30, 2024
79cb392
proof: 🌽 `SparseMerkleProof::verify()` uses `VerifyError`
cratelyn Apr 30, 2024
deecc33
proof: 🌽 `SparseMerkleRangeProof::verify()` uses `RangeProofError`
cratelyn Apr 30, 2024
89230a2
proof: 🌽 `check_compute_new_root()` uses `ComputeError`
cratelyn Apr 30, 2024
22fde81
ics23_impl: 🍐 introduce `Ics23ProofError`
cratelyn Apr 30, 2024
03cb1de
writer: 🔍 `TreeWriter` has an associated type `Error`
cratelyn May 1, 2024
cc0f649
reader: 🔎 `TreeReader` has an associated type `Error`
cratelyn May 1, 2024
d4a5de3
tree_cache: 🍓 `put_node()` uses `NodeAlreadyExists`
cratelyn May 1, 2024
9f5ef21
tree_cache: 🍓 `freeze()` uses `FreezeError`
cratelyn May 1, 2024
6b51ad7
tree_cache: 🍓 `new()` returns `TreeReader::Error`
cratelyn May 1, 2024
3119e76
restore: 🫐 `add_chunk()` uses `RestoreError`
cratelyn May 2, 2024
be4b11d
restore: 🫐 `JellyfishMerkleRestore::new()` uses `RecoveryError`
cratelyn May 2, 2024
6824f05
restore: 🫐 `JellyfishMerkleRestore::verify()` uses `VerifyError`
cratelyn May 2, 2024
c785d36
iterator: 🍒 `JellyfishMerkleIterator::next()` uses `IteratorError`
cratelyn May 3, 2024
1f6604e
tree: 🍉 `JellyfishMerkleTree::get_with_proof()` uses `LookupError`
cratelyn May 2, 2024
41f7de3
tree: 🍉 `JellyfishMerkleTree::get_range_proof()` uses `LookupError`
cratelyn May 3, 2024
6b05cc9
tree: 🍉 `JellyfishMerkleTree::create_leaf_node()` uses `NodeAlreadyEx…
cratelyn May 2, 2024
2c4357a
tree: 🍉 `JellyfishMerkleTree::put_value_sets()` uses `PutValueSetError`
cratelyn May 3, 2024
078eafa
tree: 🍉 `JellyfishMerkleTree::get_with_exclusion_proof()` uses `Looku…
cratelyn May 2, 2024
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
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,27 @@ migration = []

[dependencies]
anyhow = "1.0.38"
blake3 = { version = "1.4.0", optional = true, features = ["traits-preview"] }
borsh = { version = "1.3.0" , features = ["derive", "de_strict_order"]}
digest = "0.10"
hashbrown = "0.13.2"
hex = "0.4"
ics23 = { version = "0.11.0", optional = true}
itertools = { version = "0.10.0", default-features = false }
mirai-annotations = "1.10.1"
num-derive = "0.3.3"
num-traits = "0.2.14"
parking_lot = { version = "0.12.1", optional = true }
serde = { version = "1.0.124", features = ["derive"] }
thiserror = { version = "1.0.24", optional = true }
sha2 = { version = "0.10", optional = true }
blake3 = { version = "1.4.0", optional = true, features = ["traits-preview"] }
hex = "0.4"
thiserror = { version = "1.0.24", optional = true }
tracing = "0.1"
ics23 = { version = "0.11.0", optional = true}

[dev-dependencies]
hex = { version = "0.4", features = ["serde"] }
rand = { version = "0.8.3" }
parking_lot = { version = "0.12.1" }
serde_json = { version = "1.0.95" }
proptest = { version = "1.0.0" }
proptest-derive = { version = "0.3.0" }
rand = { version = "0.8.3" }
serde_json = { version = "1.0.95" }
sha2 = "0.10"
82 changes: 64 additions & 18 deletions src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

use alloc::{sync::Arc, vec::Vec};

use anyhow::{bail, ensure, format_err, Result};
use anyhow::{bail, ensure};

use crate::{
node_type::{Child, InternalNode, Node, NodeKey},
storage::TreeReader,
storage::{TreeReader, TreeReaderExt},
types::{
nibble::{nibble_path::NibblePath, Nibble, ROOT_NIBBLE_HEIGHT},
Version,
Expand Down Expand Up @@ -114,14 +114,35 @@ pub struct JellyfishMerkleIterator<R> {
done: bool,
}

/// Errors that can occur when iterating across a [`JellyfishMerkleIterator<R>`].
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum IteratorError<E> {
#[error(transparent)]
Read(E),
#[error("node with key `{key:?}` was missing")]
NodeMissing { key: NodeKey },
#[error(
"could not find a value at version `{version}` associated with key hash `{key_hash:?}`"
)]
ValueNotFound { version: u64, key_hash: KeyHash },
#[error("Should not reach a null node.")]
NullNodeReached,
}

impl<R> JellyfishMerkleIterator<R>
where
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
/// Constructs a new iterator. This puts the internal state in the correct position, so the
/// following `next` call will yield the smallest key that is greater or equal to
/// `starting_key`.
pub fn new(reader: Arc<R>, version: Version, starting_key: KeyHash) -> Result<Self> {
pub fn new(
reader: Arc<R>,
version: Version,
starting_key: KeyHash,
) -> Result<Self, anyhow::Error> {
let mut parent_stack = Vec::new();
let mut done = false;

Expand Down Expand Up @@ -201,7 +222,11 @@ where

/// Constructs a new iterator. This puts the internal state in the correct position, so the
/// following `next` call will yield the leaf at `start_idx`.
pub fn new_by_index(reader: Arc<R>, version: Version, start_idx: usize) -> Result<Self> {
pub fn new_by_index(
reader: Arc<R>,
version: Version,
start_idx: usize,
) -> Result<Self, anyhow::Error> {
let mut parent_stack = Vec::new();

let mut current_node_key = NodeKey::new_empty_path(version);
Expand Down Expand Up @@ -256,7 +281,7 @@ where
internal_node: &'a InternalNode,
leaves_skipped: &mut usize,
target_leaf_idx: usize,
) -> Result<(Nibble, &'a Child)> {
) -> Result<(Nibble, &'a Child), anyhow::Error> {
for (nibble, child) in internal_node.children_sorted() {
let child_leaf_count = child.leaf_count();
// n.b. The index is 0-based, so to reach leaf at N, N previous ones need to be skipped.
Expand All @@ -274,8 +299,9 @@ where
impl<R> Iterator for JellyfishMerkleIterator<R>
where
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
type Item = Result<(KeyHash, OwnedValue)>;
type Item = Result<(KeyHash, OwnedValue), IteratorError<R::Error>>;

fn next(&mut self) -> Option<Self::Item> {
if self.done {
Expand All @@ -284,27 +310,37 @@ where

if self.parent_stack.is_empty() {
let root_node_key = NodeKey::new_empty_path(self.version);
match self.reader.get_node(&root_node_key) {
Ok(Node::Leaf(leaf_node)) => {
match self
.reader
.get_node_option(&root_node_key)
.map_err(IteratorError::Read)
{
Ok(Some(Node::Leaf(leaf_node))) => {
// This means the entire tree has a single leaf node. The key of this leaf node
// is greater or equal to `starting_key` (otherwise we would have set `done` to
// true in `new`). Return the node and mark `self.done` so next time we return
// None.
self.done = true;
return match self
.reader
.get_value(root_node_key.version(), leaf_node.key_hash())
.get_value_option(root_node_key.version(), leaf_node.key_hash())
.map_err(IteratorError::Read)
{
Ok(value) => Some(Ok((leaf_node.key_hash(), value))),
Ok(Some(value)) => Some(Ok((leaf_node.key_hash(), value))),
Ok(None) => Some(Err(IteratorError::ValueNotFound {
version: root_node_key.version(),
key_hash: leaf_node.key_hash(),
})),
Err(e) => Some(Err(e)),
};
}
Ok(Node::Internal(_)) => {
Ok(Some(Node::Internal(_))) => {
// This means `starting_key` is bigger than every key in this tree, or we have
// iterated past the last key.
return None;
}
Ok(Node::Null) => unreachable!("We would have set done to true in new."),
Ok(Some(Node::Null)) => unreachable!("We would have set done to true in new."),
Ok(None) => return Some(Err(IteratorError::NodeMissing { key: root_node_key })),
Err(err) => return Some(Err(err)),
}
}
Expand All @@ -324,25 +360,35 @@ where
.version,
child_index,
);
match self.reader.get_node(&node_key) {
Ok(Node::Internal(internal_node)) => {
match self
.reader
.get_node_option(&node_key)
.map_err(IteratorError::Read)
{
Ok(Some(Node::Internal(internal_node))) => {
let visit_info = NodeVisitInfo::new(node_key, internal_node);
self.parent_stack.push(visit_info);
}
Ok(Node::Leaf(leaf_node)) => {
Ok(Some(Node::Leaf(leaf_node))) => {
return match self
.reader
.get_value(node_key.version(), leaf_node.key_hash())
.get_value_option(node_key.version(), leaf_node.key_hash())
.map_err(IteratorError::Read)
{
Ok(value) => {
Ok(Some(value)) => {
let ret = (leaf_node.key_hash(), value);
Self::cleanup_stack(&mut self.parent_stack);
Some(Ok(ret))
}
Ok(None) => Some(Err(IteratorError::ValueNotFound {
version: node_key.version(),
key_hash: leaf_node.key_hash(),
})),
Err(e) => Some(Err(e)),
}
}
Ok(Node::Null) => return Some(Err(format_err!("Should not reach a null node."))),
Ok(Some(Node::Null)) => return Some(Err(IteratorError::NullNodeReached)),
Ok(None) => return Some(Err(IteratorError::NodeMissing { key: node_key })),
Err(err) => return Some(Err(err)),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub use types::Version;
pub mod storage {
pub use node_type::{LeafNode, Node, NodeKey};
pub use reader::HasPreimage;
pub use reader::TreeReader;
pub use reader::{TreeReader, TreeReaderExt};
pub use types::nibble::nibble_path::NibblePath;
pub use writer::{
NodeBatch, NodeStats, StaleNodeIndex, StaleNodeIndexBatch, TreeUpdateBatch, TreeWriter,
Expand Down
76 changes: 47 additions & 29 deletions src/node_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,29 @@
//! [`JellyfishMerkleTree`](crate::JellyfishMerkleTree). [`InternalNode`] represents a 4-level
//! binary tree to optimize for IOPS: it compresses a tree with 31 nodes into one node with 16
//! chidren at the lowest level. [`LeafNode`] stores the full key and the value associated.
use crate::storage::TreeReader;

use crate::SimpleHasher;
use alloc::format;
use alloc::vec::Vec;
use alloc::{boxed::Box, vec};
use alloc::{boxed::Box, format, vec::Vec};

use anyhow::Context;
use borsh::{BorshDeserialize, BorshSerialize};
use num_derive::{FromPrimitive, ToPrimitive};
#[cfg(any(test))]
use proptest::prelude::*;
#[cfg(any(test))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};

use crate::proof::SparseMerkleNode;
use crate::{
storage::{TreeReader, TreeReaderExt},
types::{
nibble::{nibble_path::NibblePath, Nibble},
proof::{SparseMerkleInternalNode, SparseMerkleLeafNode},
proof::{SparseMerkleInternalNode, SparseMerkleLeafNode, SparseMerkleNode},
Version,
},
KeyHash, ValueHash, SPARSE_MERKLE_PLACEHOLDER_HASH,
KeyHash, SimpleHasher, ValueHash, SPARSE_MERKLE_PLACEHOLDER_HASH,
};

#[cfg(any(test))]
use proptest::prelude::*;
#[cfg(any(test))]
use proptest_derive::Arbitrary;

/// The unique key of each node.
#[derive(
Clone,
Expand Down Expand Up @@ -499,16 +497,21 @@ impl InternalNode {
}

/// [`build_sibling`] builds the sibling contained in the merkle tree between
/// [start; start+width) under the internal node (`self`) using the `TreeReader` as
/// a node reader to get the leaves/internal nodes at the bottom level of this internal node
fn build_sibling<H: SimpleHasher>(
/// [start; start+width) under the internal node (`self`) using the [`TreeReader`] as
/// a node reader to get the leaves/internal nodes at the bottom level of this internal node.
fn build_sibling<H, R>(
&self,
tree_reader: &impl TreeReader,
tree_reader: &R,
node_key: &NodeKey,
start: u8,
width: u8,
(existence_bitmap, leaf_bitmap): (u16, u16),
) -> SparseMerkleNode {
) -> SparseMerkleNode
where
H: SimpleHasher,
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
// Given a bit [start, 1 << nibble_height], return the value of that range.
let (range_existence_bitmap, range_leaf_bitmap) =
Self::range_bitmaps(start, width, (existence_bitmap, leaf_bitmap));
Expand Down Expand Up @@ -673,13 +676,18 @@ impl InternalNode {
/// | MSB|<---------------------- uint 16 ---------------------------->|LSB
/// height chs: `child_half_start` shs: `sibling_half_start`
/// ```
fn get_child_with_siblings_helper<H: SimpleHasher>(
fn get_child_with_siblings_helper<H, R>(
&self,
tree_reader: &impl TreeReader,
tree_reader: &R,
node_key: &NodeKey,
n: Nibble,
get_only_child: bool,
) -> (Option<NodeKey>, Vec<SparseMerkleNode>) {
) -> (Option<NodeKey>, Vec<SparseMerkleNode>)
where
H: SimpleHasher,
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
let mut siblings: Vec<SparseMerkleNode> = vec![];
let (existence_bitmap, leaf_bitmap) = self.generate_bitmaps();

Expand All @@ -692,7 +700,7 @@ impl InternalNode {
let width = 1 << h;
let (child_half_start, sibling_half_start) = get_child_and_sibling_half_start(n, h);
// Compute the root hash of the subtree rooted at the sibling of `r`.
siblings.push(self.build_sibling::<H>(
siblings.push(self.build_sibling::<H, R>(
tree_reader,
node_key,
sibling_half_start,
Expand Down Expand Up @@ -762,13 +770,18 @@ impl InternalNode {

/// [`get_child_with_siblings`] will return the child from this subtree that matches the nibble n in addition
/// to building the list of its sibblings. This function has the same behavior as [`child`].
pub(crate) fn get_child_with_siblings<H: SimpleHasher>(
pub(crate) fn get_child_with_siblings<H, R>(
&self,
tree_cache: &impl TreeReader,
tree_cache: &R,
node_key: &NodeKey,
n: Nibble,
) -> (Option<NodeKey>, Vec<SparseMerkleNode>) {
self.get_child_with_siblings_helper::<H>(tree_cache, node_key, n, false)
) -> (Option<NodeKey>, Vec<SparseMerkleNode>)
where
H: SimpleHasher,
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
self.get_child_with_siblings_helper::<H, R>(tree_cache, node_key, n, false)
}

/// [`get_only_child_with_siblings`] will **either** return the child that matches the nibble n or the only
Expand All @@ -777,13 +790,18 @@ impl InternalNode {
/// Even this leaf child is not the n-th child, it should be returned instead of
/// `None` because it's existence indirectly proves the n-th child doesn't exist.
/// Please read proof format for details.
pub(crate) fn get_only_child_with_siblings<H: SimpleHasher>(
pub(crate) fn get_only_child_with_siblings<H: SimpleHasher, R>(
&self,
tree_reader: &impl TreeReader,
tree_reader: &R,
node_key: &NodeKey,
n: Nibble,
) -> (Option<NodeKey>, Vec<SparseMerkleNode>) {
self.get_child_with_siblings_helper::<H>(tree_reader, node_key, n, true)
) -> (Option<NodeKey>, Vec<SparseMerkleNode>)
where
H: SimpleHasher,
R: TreeReader,
<R as TreeReader>::Error: std::error::Error + Send + Sync + 'static,
{
self.get_child_with_siblings_helper::<H, R>(tree_reader, node_key, n, true)
}

#[cfg(test)]
Expand Down
Loading
Loading