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
11 changes: 9 additions & 2 deletions crates/starknet_committer/src/db/facts_db/traversal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;

use starknet_api::hash::HashOutput;
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
use starknet_patricia::patricia_merkle_tree::filled_tree::node_serde::FactNodeDeserializationContext;
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
NodeData,
Preimage,
Expand All @@ -10,7 +11,7 @@ use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, TraversalResult};
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
use starknet_patricia_storage::db_object::HasStaticPrefix;
use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix};
use starknet_patricia_storage::errors::StorageError;
use starknet_patricia_storage::storage_trait::{create_db_key, DbKey, Storage};

Expand Down Expand Up @@ -40,7 +41,13 @@ pub async fn calculate_subtrees_roots<'a, L: Leaf>(
let db_vals = storage.mget(&db_keys.iter().collect::<Vec<&DbKey>>()).await?;
for ((subtree, optional_val), db_key) in subtrees.iter().zip(db_vals.iter()).zip(db_keys) {
let Some(val) = optional_val else { Err(StorageError::MissingKey(db_key))? };
subtrees_roots.push(FilledNode::deserialize(subtree.root_hash, val, subtree.is_leaf())?)
subtrees_roots.push(FilledNode::deserialize(
val,
&FactNodeDeserializationContext {
is_leaf: subtree.is_leaf(),
node_hash: subtree.root_hash,
},
)?)
}
Ok(subtrees_roots)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde_json::Value;
use starknet_api::core::{ClassHash, Nonce};
use starknet_api::hash::HashOutput;
use starknet_patricia::patricia_merkle_tree::types::SubTreeHeight;
use starknet_patricia_storage::db_object::{DBObject, Deserializable};
use starknet_patricia_storage::db_object::{DBObject, EmptyDeserializationContext};
use starknet_patricia_storage::errors::DeserializationError;
use starknet_patricia_storage::storage_trait::{DbKeyPrefix, DbValue};
use starknet_types_core::felt::Felt;
Expand All @@ -31,21 +31,46 @@ impl From<CommitterLeafPrefix> for DbKeyPrefix {
}

impl DBObject for StarknetStorageValue {
type DeserializeContext = EmptyDeserializationContext;

/// Serializes the value into a 32-byte vector.
fn serialize(&self) -> DbValue {
DbValue(self.0.to_bytes_be().to_vec())
}

fn deserialize(
value: &DbValue,
_deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError> {
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
}
}

impl DBObject for CompiledClassHash {
type DeserializeContext = EmptyDeserializationContext;

/// Creates a json string describing the leaf and casts it into a byte vector.
fn serialize(&self) -> DbValue {
let json_string = format!(r#"{{"compiled_class_hash": "{}"}}"#, self.0.to_hex_string());
DbValue(json_string.into_bytes())
}

fn deserialize(
value: &DbValue,
_deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError> {
let json_str = std::str::from_utf8(&value.0)?;
let map: HashMap<String, String> = serde_json::from_str(json_str)?;
let hash_as_hex = map
.get("compiled_class_hash")
.ok_or(DeserializationError::NonExistingKey("compiled_class_hash".to_string()))?;
Ok(Self::from_hex(hash_as_hex)?)
}
}

impl DBObject for ContractState {
type DeserializeContext = EmptyDeserializationContext;

/// Creates a json string describing the leaf and casts it into a byte vector.
fn serialize(&self) -> DbValue {
let json_string = format!(
Expand All @@ -57,27 +82,11 @@ impl DBObject for ContractState {
);
DbValue(json_string.into_bytes())
}
}

impl Deserializable for StarknetStorageValue {
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
}
}

impl Deserializable for CompiledClassHash {
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
let json_str = std::str::from_utf8(&value.0)?;
let map: HashMap<String, String> = serde_json::from_str(json_str)?;
let hash_as_hex = map
.get("compiled_class_hash")
.ok_or(DeserializationError::NonExistingKey("compiled_class_hash".to_string()))?;
Ok(Self::from_hex(hash_as_hex)?)
}
}

impl Deserializable for ContractState {
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
fn deserialize(
value: &DbValue,
_deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError> {
let json_str = std::str::from_utf8(&value.0)?;
let deserialized_map: Value = serde_json::from_str(json_str)?;
let get_leaf_key = |map: &Value, key: &str| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use starknet_api::core::{ClassHash, Nonce};
use starknet_api::felt;
use starknet_api::hash::HashOutput;
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
use starknet_patricia_storage::db_object::Deserializable;
use starknet_patricia_storage::db_object::{DBObject, EmptyDeserializationContext};
use starknet_patricia_storage::storage_trait::DbValue;
use starknet_types_core::felt::Felt;

Expand All @@ -32,7 +32,7 @@ use crate::patricia_merkle_tree::types::CompiledClassHash;
]
fn test_leaf_serde<L: Leaf + Eq + Debug>(#[case] leaf: L) {
let serialized = leaf.serialize();
let deserialized = L::deserialize(&serialized).unwrap();
let deserialized = L::deserialize(&serialized, &EmptyDeserializationContext).unwrap();
assert_eq!(deserialized, leaf);
}

Expand Down Expand Up @@ -60,7 +60,8 @@ fn test_deserialize_contract_state_without_nonce() {
.to_vec(),
);

let contract_state = ContractState::deserialize(&serialized).unwrap();
let contract_state =
ContractState::deserialize(&serialized, &EmptyDeserializationContext).unwrap();

// Validate the fields (nonce should be the default "0")
assert_eq!(contract_state.nonce, Nonce(Felt::ZERO));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rand::Rng;
use starknet_api::hash::HashOutput;
use starknet_patricia_storage::db_object::{
DBObject,
Deserializable,
EmptyDeserializationContext,
EmptyKeyContext,
HasStaticPrefix,
};
Expand Down Expand Up @@ -37,13 +37,16 @@ impl HasStaticPrefix for MockLeaf {
}

impl DBObject for MockLeaf {
type DeserializeContext = EmptyDeserializationContext;

fn serialize(&self) -> DbValue {
DbValue(self.0.to_bytes_be().to_vec())
}
}

impl Deserializable for MockLeaf {
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
fn deserialize(
value: &DbValue,
_deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError> {
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use ethnum::U256;
use starknet_api::hash::HashOutput;
use starknet_patricia_storage::db_object::{DBObject, HasDynamicPrefix, HasStaticPrefix};
use starknet_patricia_storage::db_object::{
DBObject,
EmptyDeserializationContext,
HasDynamicPrefix,
HasStaticPrefix,
};
use starknet_patricia_storage::errors::DeserializationError;
use starknet_patricia_storage::storage_trait::{DbKey, DbKeyPrefix, DbValue};
use starknet_types_core::felt::Felt;
Expand Down Expand Up @@ -53,16 +58,24 @@ impl<L: Leaf> HasDynamicPrefix for FilledNode<L> {
// Inherit the KeyContext from the HasStaticPrefix implementation of the leaf.
type KeyContext = <L as HasStaticPrefix>::KeyContext;

fn get_prefix(&self, key_context: &Self::KeyContext) -> DbKeyPrefix {
fn get_prefix(&self, _key_context: &Self::KeyContext) -> DbKeyPrefix {
match &self.data {
NodeData::Binary(_) | NodeData::Edge(_) => PatriciaPrefix::InnerNode,
NodeData::Leaf(_) => PatriciaPrefix::Leaf(L::get_static_prefix(key_context)),
NodeData::Leaf(_) => PatriciaPrefix::Leaf(L::get_static_prefix(_key_context)),
}
.into()
}
}

/// Extra context required to deserialize [FilledNode<L, HashOutput>].
/// See [DBObject::DeserializeContext] for more information
pub struct FactNodeDeserializationContext {
pub is_leaf: bool,
pub node_hash: HashOutput,
}

impl<L: Leaf> DBObject for FilledNode<L> {
type DeserializeContext = FactNodeDeserializationContext;
/// This method serializes the filled node into a byte vector, where:
/// - For binary nodes: Concatenates left and right hashes.
/// - For edge nodes: Concatenates bottom hash, path, and path length.
Expand Down Expand Up @@ -94,22 +107,21 @@ impl<L: Leaf> DBObject for FilledNode<L> {
NodeData::Leaf(leaf_data) => leaf_data.serialize(),
}
}
}

impl<L: Leaf> FilledNode<L> {
/// Deserializes filled nodes.
pub fn deserialize(
node_hash: HashOutput,
fn deserialize(
value: &DbValue,
is_leaf: bool,
deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError> {
if is_leaf {
return Ok(Self { hash: node_hash, data: NodeData::Leaf(L::deserialize(value)?) });
if deserialize_context.is_leaf {
return Ok(Self {
hash: deserialize_context.node_hash,
data: NodeData::Leaf(L::deserialize(value, &EmptyDeserializationContext)?),
});
}

if value.0.len() == BINARY_BYTES {
Ok(Self {
hash: node_hash,
hash: deserialize_context.node_hash,
data: NodeData::Binary(BinaryData {
left_hash: HashOutput(Felt::from_bytes_be_slice(
&value.0[..SERIALIZE_HASH_BYTES],
Expand All @@ -129,7 +141,7 @@ impl<L: Leaf> FilledNode<L> {
BINARY_BYTES
);
Ok(Self {
hash: node_hash,
hash: deserialize_context.node_hash,
data: NodeData::Edge(EdgeData {
bottom_hash: HashOutput(Felt::from_bytes_be_slice(
&value.0[..SERIALIZE_HASH_BYTES],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use std::collections::HashMap;
use std::fmt::Debug;
use std::future::Future;

use starknet_patricia_storage::db_object::{DBObject, Deserializable, HasStaticPrefix};
use starknet_patricia_storage::db_object::{
DBObject,
EmptyDeserializationContext,
HasStaticPrefix,
};
use starknet_types_core::felt::Felt;

use crate::patricia_merkle_tree::node_data::errors::{LeafError, LeafResult};
Expand All @@ -11,7 +15,14 @@ use crate::patricia_merkle_tree::original_skeleton_tree::tree::OriginalSkeletonT
use crate::patricia_merkle_tree::types::NodeIndex;

pub trait Leaf:
Clone + Sync + Send + HasStaticPrefix + DBObject + Deserializable + Default + Debug + Eq
Clone
+ Sync
+ Send
+ HasStaticPrefix
+ DBObject<DeserializeContext = EmptyDeserializationContext>
+ Default
+ Debug
+ Eq
{
// TODO(Amos, 1/1/2025): When default values for associated types are stable - use them, and
// add a default implementation for `create`.
Expand Down
22 changes: 15 additions & 7 deletions crates/starknet_patricia_storage/src/db_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,25 @@ impl<T: HasStaticPrefix> HasDynamicPrefix for T {
}
}

pub trait DBObject: HasDynamicPrefix {
pub struct EmptyDeserializationContext;

pub trait DBObject: Sized + HasDynamicPrefix {
/// Extra data needed to deserialize the object. For example, facts layout nodes need the node
/// hash and an indication of whether or not it's a leaf node (index layout nodes only need the
/// latter).
type DeserializeContext;

/// Serializes the given value.
fn serialize(&self) -> DbValue;

/// Returns a `DbKey` from a prefix and a suffix.
/// Deserializes the given value using the provided context.
fn deserialize(
value: &DbValue,
deserialize_context: &Self::DeserializeContext,
) -> Result<Self, DeserializationError>;

/// Returns a [DbKey] from a prefix and a suffix.
fn get_db_key(&self, key_context: &Self::KeyContext, suffix: &[u8]) -> DbKey {
create_db_key(self.get_prefix(key_context), suffix)
}
}

pub trait Deserializable: Sized {
/// Deserializes the given value.
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError>;
}
Loading