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 cccc0dc
Show file tree
Hide file tree
Showing 13 changed files with 647 additions and 15 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

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
6 changes: 5 additions & 1 deletion crates/committer/src/patricia_merkle_tree/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// TODO(Amos, 01/04/2024): Add error types.
#[derive(Debug)]
pub(crate) enum OriginalSkeletonTreeError {}
#[allow(dead_code)]
pub(crate) enum OriginalSkeletonTreeError {
Deserialization(String),
StorageRead,
}

#[derive(Debug)]
pub(crate) enum UpdatedSkeletonTreeError {
Expand Down
49 changes: 48 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,25 @@
use crate::patricia_merkle_tree::types::{EdgeData, LeafDataTrait};
use crate::storage::storage_trait::{StorageKey, StorageValue};
use crate::{hash::types::HashOutput, types::Felt};

use super::errors::OriginalSkeletonTreeError;
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 +37,7 @@ pub(crate) struct BinaryData {
}

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum LeafData {
StorageValue(Felt),
CompiledClassHash(ClassHash),
Expand Down Expand Up @@ -55,3 +65,40 @@ 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 Aviv's PR 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 Aviv's PR 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(Nimrod, 10/5/2024): Allow leaf deserialization (it's not required for now).
Err(OriginalSkeletonTreeError::Deserialization(
"Could not deserialize given key and value.".to_string(),
))
}
}
}
187 changes: 187 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,187 @@
use bisection::{bisect_left, bisect_right};

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::StoragePrefix;
use crate::storage::storage_trait::{StorageKey, StorageValue};
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: StorageKey,
}
#[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.shift_left(1)) + NodeIndex(Felt::ONE)).shift_left(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 {
/// Function's purpose is to fetch from storage the patricia witnesses in order to build the
/// original skeleton tree. Given a list of subtrees, traverses towards their leaves and
/// fetches all non-empty and sibiling nodes. Assumes no subtrees of height 0 (leaves).
fn fetch_nodes(
&mut self,
subtrees: Vec<SubTree<'_>>,
storage: HashMap<StorageKey, StorageValue>,
// TODO(Nimrod, 25/4/2024): Change input type to Storage once Amos has implemented the
// Storage trait for HashMap.
) -> OriginalSkeletonTreeResult<()> {
if subtrees.is_empty() {
return Ok(());
}
let mut next_subtrees = Vec::new();
let mut filled_nodes = Vec::new();
for subtree in subtrees.iter() {
let val = storage
.get(&subtree.root_hash.with_prefix(StoragePrefix::PatriciaNode))
.ok_or(OriginalSkeletonTreeError::StorageRead)?;
filled_nodes.push(FilledNode::deserialize(&subtree.root_hash, val)?)
}
for (filled_node, subtree) in filled_nodes.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.shift_left(1);
let left_subtree = SubTree {
sorted_leaf_indices: left_leaves,
root_index: left_root_index,
root_hash: StorageKey::from(left_hash.0),
};
let right_subtree = SubTree {
sorted_leaf_indices: right_leaves,
root_index: left_root_index + NodeIndex(Felt::ONE),
root_hash: StorageKey::from(right_hash.0),
};
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.shift_left(bottom_height.0);
let rightmost_in_subtree = leftmost_in_subtree
+ (NodeIndex(Felt::ONE).shift_left(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: StorageKey::from(bottom_hash.0),
};
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 cccc0dc

Please sign in to comment.