Skip to content

Commit

Permalink
chore: building original skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
nimrod-starkware committed Apr 17, 2024
1 parent ac3f787 commit e934dd2
Show file tree
Hide file tree
Showing 14 changed files with 683 additions and 16 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ license = "Apache-2.0"
license-file = "LICENSE"

[workspace.dependencies]
bisection = "0.1.0"
clap = { version = "4.5.4", features = ["cargo", "derive"] }
derive_more = "0.99.17"
pretty_assertions = "1.2.1"
Expand Down
2 changes: 2 additions & 0 deletions crates/committer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ pretty_assertions.workspace = true
rstest.workspace = true

[dependencies]
bisection.workspace = true
derive_more.workspace = true
starknet-types-core.workspace = true

2 changes: 1 addition & 1 deletion crates/committer/src/hash/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::types::Felt;
pub(crate) struct HashInputPair(pub Felt, pub Felt);

#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub(crate) struct HashOutput(pub Felt);

#[allow(dead_code)]
Expand Down
1 change: 1 addition & 0 deletions crates/committer/src/patricia_merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod errors;
pub mod filled_node;
pub mod filled_tree;
pub mod original_skeleton_calc;
pub mod original_skeleton_node;
pub mod original_skeleton_tree;
pub mod types;
Expand Down
7 changes: 6 additions & 1 deletion crates/committer/src/patricia_merkle_tree/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// TODO(Amos, 01/04/2024): Add error types.
#[derive(Debug)]
pub(crate) enum OriginalSkeletonTreeError {}
#[allow(dead_code)]
pub(crate) enum OriginalSkeletonTreeError {
// TODO(Nimrod, 5/5/2024): If possible, divide Deserialization to more specific types of errors.
Deserialization(String),
StorageRead,
}

#[derive(Debug)]
pub(crate) enum UpdatedSkeletonTreeError {
Expand Down
45 changes: 44 additions & 1 deletion crates/committer/src/patricia_merkle_tree/filled_node.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
use crate::patricia_merkle_tree::types::{EdgeData, LeafDataTrait};
use crate::storage::storage_trait::{StorageKey, StorageValue};
use crate::{hash::types::HashOutput, types::Felt};

use super::original_skeleton_tree::OriginalSkeletonTreeResult;
use super::types::{EdgePath, EdgePathLength, PathToBottom};

// TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented.
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ClassHash(pub Felt);

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Nonce(pub Felt);

#[allow(dead_code)]
/// A node in a Patricia-Merkle tree which was modified during an update.
pub(crate) struct FilledNode<L: LeafDataTrait> {
pub(crate) hash: HashOutput,
data: NodeData<L>,
pub(crate) data: NodeData<L>,
}

#[allow(dead_code)]
Expand All @@ -28,6 +36,7 @@ pub(crate) struct BinaryData {
}

#[allow(dead_code)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum LeafData {
StorageValue(Felt),
CompiledClassHash(ClassHash),
Expand Down Expand Up @@ -55,3 +64,37 @@ impl LeafDataTrait for LeafData {
}
}
}

#[allow(dead_code)]
impl FilledNode<LeafData> {
pub(crate) fn deserialize(
key: &StorageKey,
value: &StorageValue,
) -> OriginalSkeletonTreeResult<Self> {
// TODO(Nimrod, 30/4/2024): Compare to constant values once [PR #18] (https://github.com/starkware-libs/committer/pull/18) is merged.
if value.0.len() == 64 {
Ok(Self {
hash: HashOutput(Felt::from_bytes_be_slice(&key.0)),
data: NodeData::Binary(BinaryData {
left_hash: HashOutput(Felt::from_bytes_be_slice(&value.0[..32])),
right_hash: HashOutput(Felt::from_bytes_be_slice(&value.0[32..])),
}),
})
}
// TODO(Nimrod, 30/4/2024): Compare to constant values once [PR #18] (https://github.com/starkware-libs/committer/pull/18) is merged.
else if value.0.len() == 65 {
return Ok(Self {
hash: HashOutput(Felt::from_bytes_be_slice(&key.0)),
data: NodeData::Edge(EdgeData {
bottom_hash: HashOutput(Felt::from_bytes_be_slice(&value.0[..32])),
path_to_bottom: PathToBottom {
path: EdgePath(Felt::from_bytes_be_slice(&value.0[32..64])),
length: EdgePathLength(value.0[64]),
},
}),
});
} else {
todo!("Deserialize leaves.")
}
}
}
196 changes: 196 additions & 0 deletions crates/committer/src/patricia_merkle_tree/original_skeleton_calc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
use bisection::{bisect_left, bisect_right};

use crate::hash::types::HashOutput;
use crate::patricia_merkle_tree::errors::OriginalSkeletonTreeError;
use crate::patricia_merkle_tree::filled_node::BinaryData;
use crate::patricia_merkle_tree::filled_node::FilledNode;
use crate::patricia_merkle_tree::filled_node::NodeData;
use crate::patricia_merkle_tree::original_skeleton_tree::OriginalSkeletonTreeResult;
use crate::patricia_merkle_tree::types::EdgeData;
use crate::patricia_merkle_tree::types::TreeHeight;
use crate::patricia_merkle_tree::{
filled_node::LeafData, original_skeleton_node::OriginalSkeletonNode, types::NodeIndex,
};
use crate::storage::storage_trait::Storage;
use crate::storage::storage_trait::StorageKey;
use crate::storage::storage_trait::StoragePrefix;
use crate::types::Felt;
use std::collections::HashMap;

#[cfg(test)]
#[path = "original_skeleton_calc_test.rs"]
pub mod original_skeleton_calc_test;

#[allow(dead_code)]
pub(crate) struct OriginalSkeletonTreeImpl {
nodes: HashMap<NodeIndex, OriginalSkeletonNode<LeafData>>,
leaf_modifications: HashMap<NodeIndex, LeafData>,
tree_height: TreeHeight,
}

#[allow(dead_code)]
struct SubTree<'a> {
pub sorted_leaf_indices: &'a [NodeIndex],
pub root_index: NodeIndex,
pub root_hash: HashOutput,
}

#[allow(dead_code)]
impl<'a> SubTree<'a> {
pub(crate) fn get_height(&self, total_tree_height: &TreeHeight) -> TreeHeight {
TreeHeight(total_tree_height.0 - self.root_index.0.bits() + 1)
}

pub(crate) fn split_leaves(
&self,
total_tree_height: &TreeHeight,
) -> (&'a [NodeIndex], &'a [NodeIndex]) {
let height = self.get_height(total_tree_height);
let leftmost_index_in_right_subtree = ((self.root_index.times_two_to_the_power(1))
+ NodeIndex(Felt::ONE))
.times_two_to_the_power(height.0 - 1);
let mid = bisect_left(self.sorted_leaf_indices, &leftmost_index_in_right_subtree);
(
&self.sorted_leaf_indices[..mid],
&self.sorted_leaf_indices[mid..],
)
}

pub(crate) fn is_sibling(&self) -> bool {
self.sorted_leaf_indices.is_empty()
}
}

#[allow(dead_code)]
impl OriginalSkeletonTreeImpl {
/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage.
/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty and
/// sibling nodes. Assumes no subtrees of height 0 (leaves).

fn fetch_nodes(
&mut self,
subtrees: Vec<SubTree<'_>>,
storage: &impl Storage,
) -> OriginalSkeletonTreeResult<()> {
if subtrees.is_empty() {
return Ok(());
}
let mut next_subtrees = Vec::new();
let mut subtrees_roots = Vec::new();
for subtree in subtrees.iter() {
let val = storage
.get(
&StorageKey::from(subtree.root_hash.0).with_prefix(StoragePrefix::PatriciaNode),
)
.ok_or(OriginalSkeletonTreeError::StorageRead)?;
subtrees_roots.push(FilledNode::deserialize(
&StorageKey::from(subtree.root_hash.0),
&val,
)?)
}
for (filled_node, subtree) in subtrees_roots.into_iter().zip(subtrees.iter()) {
match filled_node.data {
// Binary node.
NodeData::Binary(BinaryData {
left_hash,
right_hash,
}) => {
if subtree.is_sibling() {
self.nodes.insert(
subtree.root_index,
OriginalSkeletonNode::LeafOrBinarySibling(filled_node.hash),
);
continue;
}
self.nodes
.insert(subtree.root_index, OriginalSkeletonNode::Binary);
let (left_leaves, right_leaves) = subtree.split_leaves(&self.tree_height);
let left_root_index = subtree.root_index.times_two_to_the_power(1);
let left_subtree = SubTree {
sorted_leaf_indices: left_leaves,
root_index: left_root_index,
root_hash: left_hash,
};
let right_subtree = SubTree {
sorted_leaf_indices: right_leaves,
root_index: left_root_index + NodeIndex(Felt::ONE),
root_hash: right_hash,
};
if subtree.get_height(&self.tree_height).is_of_height_one() {
// Children are leaves.
if left_subtree.is_sibling() {
self.nodes.insert(
left_subtree.root_index,
OriginalSkeletonNode::LeafOrBinarySibling(left_hash),
);
}
if right_subtree.is_sibling() {
self.nodes.insert(
right_subtree.root_index,
OriginalSkeletonNode::LeafOrBinarySibling(right_hash),
);
}
continue;
}
next_subtrees.extend(vec![left_subtree, right_subtree]);
}
// Edge node.
NodeData::Edge(EdgeData {
bottom_hash,
path_to_bottom,
}) => {
if subtree.is_sibling() {
// Sibling will remain an edge node. No need to open the bottom.
self.nodes.insert(
subtree.root_index,
OriginalSkeletonNode::EdgeSibling(EdgeData {
bottom_hash,
path_to_bottom,
}),
);
continue;
}
// Parse bottom.
let bottom_index = path_to_bottom.bottom_index(subtree.root_index);
let bottom_height =
subtree.get_height(&self.tree_height) - TreeHeight(path_to_bottom.length.0);
let leftmost_in_subtree = bottom_index.times_two_to_the_power(bottom_height.0);
let rightmost_in_subtree = leftmost_in_subtree
+ (NodeIndex(Felt::ONE).times_two_to_the_power(bottom_height.0))
- NodeIndex(Felt::ONE);
let bottom_leaves = &subtree.sorted_leaf_indices[bisect_left(
subtree.sorted_leaf_indices,
&leftmost_in_subtree,
)
..bisect_right(subtree.sorted_leaf_indices, &rightmost_in_subtree)];
self.nodes.insert(
subtree.root_index,
OriginalSkeletonNode::Edge { path_to_bottom },
);
if bottom_height.is_leaf_height() {
if bottom_leaves.is_empty() {
// Bottom is a leaf sibling.
self.nodes.insert(
bottom_index,
OriginalSkeletonNode::LeafOrBinarySibling(bottom_hash),
);
}
continue;
}
let bottom_subtree = SubTree {
sorted_leaf_indices: bottom_leaves,
root_index: bottom_index,
root_hash: bottom_hash,
};
next_subtrees.push(bottom_subtree);
}
NodeData::Leaf(_) => {
return Err(OriginalSkeletonTreeError::Deserialization(
"Fetch nodes called on a leaf subtree. Should not happen.".to_string(),
))
}
}
}
self.fetch_nodes(next_subtrees, storage)
}
}
Loading

0 comments on commit e934dd2

Please sign in to comment.