Skip to content

Commit 76dfba8

Browse files
committed
starknet_committer,starknet_patricia: add deserialization context
1 parent ae2e4a5 commit 76dfba8

File tree

7 files changed

+101
-54
lines changed

7 files changed

+101
-54
lines changed

crates/starknet_committer/src/db/facts_db/traversal.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::HashMap;
22

33
use starknet_api::hash::HashOutput;
44
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
5+
use starknet_patricia::patricia_merkle_tree::filled_tree::node_serde::FactNodeDeserializationContext;
56
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
67
NodeData,
78
Preimage,
@@ -10,7 +11,7 @@ use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
1011
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
1112
use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, TraversalResult};
1213
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
13-
use starknet_patricia_storage::db_object::HasStaticPrefix;
14+
use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix};
1415
use starknet_patricia_storage::errors::StorageError;
1516
use starknet_patricia_storage::storage_trait::{create_db_key, DbKey, Storage};
1617

@@ -40,7 +41,13 @@ pub async fn calculate_subtrees_roots<'a, L: Leaf>(
4041
let db_vals = storage.mget(&db_keys.iter().collect::<Vec<&DbKey>>()).await?;
4142
for ((subtree, optional_val), db_key) in subtrees.iter().zip(db_vals.iter()).zip(db_keys) {
4243
let Some(val) = optional_val else { Err(StorageError::MissingKey(db_key))? };
43-
subtrees_roots.push(FilledNode::deserialize(subtree.root_hash, val, subtree.is_leaf())?)
44+
subtrees_roots.push(FilledNode::deserialize(
45+
val,
46+
&FactNodeDeserializationContext {
47+
is_leaf: subtree.is_leaf(),
48+
node_hash: subtree.root_hash,
49+
},
50+
)?)
4451
}
4552
Ok(subtrees_roots)
4653
}

crates/starknet_committer/src/patricia_merkle_tree/leaf/leaf_serde.rs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use serde_json::Value;
44
use starknet_api::core::{ClassHash, Nonce};
55
use starknet_api::hash::HashOutput;
66
use starknet_patricia::patricia_merkle_tree::types::SubTreeHeight;
7-
use starknet_patricia_storage::db_object::{DBObject, Deserializable};
7+
use starknet_patricia_storage::db_object::{DBObject, EmptyDeserializationContext};
88
use starknet_patricia_storage::errors::DeserializationError;
99
use starknet_patricia_storage::storage_trait::{DbKeyPrefix, DbValue};
1010
use starknet_types_core::felt::Felt;
@@ -31,21 +31,46 @@ impl From<CommitterLeafPrefix> for DbKeyPrefix {
3131
}
3232

3333
impl DBObject for StarknetStorageValue {
34+
type DeserializeContext = EmptyDeserializationContext;
35+
3436
/// Serializes the value into a 32-byte vector.
3537
fn serialize(&self) -> DbValue {
3638
DbValue(self.0.to_bytes_be().to_vec())
3739
}
40+
41+
fn deserialize(
42+
value: &DbValue,
43+
_deserialize_context: &Self::DeserializeContext,
44+
) -> Result<Self, DeserializationError> {
45+
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
46+
}
3847
}
3948

4049
impl DBObject for CompiledClassHash {
50+
type DeserializeContext = EmptyDeserializationContext;
51+
4152
/// Creates a json string describing the leaf and casts it into a byte vector.
4253
fn serialize(&self) -> DbValue {
4354
let json_string = format!(r#"{{"compiled_class_hash": "{}"}}"#, self.0.to_hex_string());
4455
DbValue(json_string.into_bytes())
4556
}
57+
58+
fn deserialize(
59+
value: &DbValue,
60+
_deserialize_context: &Self::DeserializeContext,
61+
) -> Result<Self, DeserializationError> {
62+
let json_str = std::str::from_utf8(&value.0)?;
63+
let map: HashMap<String, String> = serde_json::from_str(json_str)?;
64+
let hash_as_hex = map
65+
.get("compiled_class_hash")
66+
.ok_or(DeserializationError::NonExistingKey("compiled_class_hash".to_string()))?;
67+
Ok(Self::from_hex(hash_as_hex)?)
68+
}
4669
}
4770

4871
impl DBObject for ContractState {
72+
type DeserializeContext = EmptyDeserializationContext;
73+
4974
/// Creates a json string describing the leaf and casts it into a byte vector.
5075
fn serialize(&self) -> DbValue {
5176
let json_string = format!(
@@ -57,27 +82,11 @@ impl DBObject for ContractState {
5782
);
5883
DbValue(json_string.into_bytes())
5984
}
60-
}
61-
62-
impl Deserializable for StarknetStorageValue {
63-
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
64-
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
65-
}
66-
}
67-
68-
impl Deserializable for CompiledClassHash {
69-
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
70-
let json_str = std::str::from_utf8(&value.0)?;
71-
let map: HashMap<String, String> = serde_json::from_str(json_str)?;
72-
let hash_as_hex = map
73-
.get("compiled_class_hash")
74-
.ok_or(DeserializationError::NonExistingKey("compiled_class_hash".to_string()))?;
75-
Ok(Self::from_hex(hash_as_hex)?)
76-
}
77-
}
7885

79-
impl Deserializable for ContractState {
80-
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
86+
fn deserialize(
87+
value: &DbValue,
88+
_deserialize_context: &Self::DeserializeContext,
89+
) -> Result<Self, DeserializationError> {
8190
let json_str = std::str::from_utf8(&value.0)?;
8291
let deserialized_map: Value = serde_json::from_str(json_str)?;
8392
let get_leaf_key = |map: &Value, key: &str| {

crates/starknet_committer/src/patricia_merkle_tree/leaf/leaf_serde_test.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use starknet_api::core::{ClassHash, Nonce};
55
use starknet_api::felt;
66
use starknet_api::hash::HashOutput;
77
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
8-
use starknet_patricia_storage::db_object::Deserializable;
8+
use starknet_patricia_storage::db_object::{DBObject, EmptyDeserializationContext};
99
use starknet_patricia_storage::storage_trait::DbValue;
1010
use starknet_types_core::felt::Felt;
1111

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

@@ -60,7 +60,8 @@ fn test_deserialize_contract_state_without_nonce() {
6060
.to_vec(),
6161
);
6262

63-
let contract_state = ContractState::deserialize(&serialized).unwrap();
63+
let contract_state =
64+
ContractState::deserialize(&serialized, &EmptyDeserializationContext).unwrap();
6465

6566
// Validate the fields (nonce should be the default "0")
6667
assert_eq!(contract_state.nonce, Nonce(Felt::ZERO));

crates/starknet_patricia/src/patricia_merkle_tree/external_test_utils.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rand::Rng;
66
use starknet_api::hash::HashOutput;
77
use starknet_patricia_storage::db_object::{
88
DBObject,
9-
Deserializable,
9+
EmptyDeserializationContext,
1010
EmptyKeyContext,
1111
HasStaticPrefix,
1212
};
@@ -37,13 +37,16 @@ impl HasStaticPrefix for MockLeaf {
3737
}
3838

3939
impl DBObject for MockLeaf {
40+
type DeserializeContext = EmptyDeserializationContext;
41+
4042
fn serialize(&self) -> DbValue {
4143
DbValue(self.0.to_bytes_be().to_vec())
4244
}
43-
}
4445

45-
impl Deserializable for MockLeaf {
46-
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
46+
fn deserialize(
47+
value: &DbValue,
48+
_deserialize_context: &Self::DeserializeContext,
49+
) -> Result<Self, DeserializationError> {
4750
Ok(Self(Felt::from_bytes_be_slice(&value.0)))
4851
}
4952
}

crates/starknet_patricia/src/patricia_merkle_tree/filled_tree/node_serde.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use ethnum::U256;
22
use starknet_api::hash::HashOutput;
3-
use starknet_patricia_storage::db_object::{DBObject, HasDynamicPrefix, HasStaticPrefix};
3+
use starknet_patricia_storage::db_object::{
4+
DBObject,
5+
EmptyDeserializationContext,
6+
HasDynamicPrefix,
7+
HasStaticPrefix,
8+
};
49
use starknet_patricia_storage::errors::DeserializationError;
510
use starknet_patricia_storage::storage_trait::{DbKey, DbKeyPrefix, DbValue};
611
use starknet_types_core::felt::Felt;
@@ -52,17 +57,24 @@ impl<L: Leaf> FilledNode<L> {
5257
impl<L: Leaf> HasDynamicPrefix for FilledNode<L> {
5358
// Inherit the KeyContext from the HasStaticPrefix implementation of the leaf.
5459
type KeyContext = <L as HasStaticPrefix>::KeyContext;
55-
56-
fn get_prefix(&self, key_context: &Self::KeyContext) -> DbKeyPrefix {
60+
fn get_prefix(&self, _key_context: &Self::KeyContext) -> DbKeyPrefix {
5761
match &self.data {
5862
NodeData::Binary(_) | NodeData::Edge(_) => PatriciaPrefix::InnerNode,
59-
NodeData::Leaf(_) => PatriciaPrefix::Leaf(L::get_static_prefix(key_context)),
63+
NodeData::Leaf(_) => PatriciaPrefix::Leaf(L::get_static_prefix(_key_context)),
6064
}
6165
.into()
6266
}
6367
}
6468

69+
/// Extra context required to deserialize [FilledNode<L, HashOutput>].
70+
/// See [DBObject::DeserializeContext] for more information
71+
pub struct FactNodeDeserializationContext {
72+
pub is_leaf: bool,
73+
pub node_hash: HashOutput,
74+
}
75+
6576
impl<L: Leaf> DBObject for FilledNode<L> {
77+
type DeserializeContext = FactNodeDeserializationContext;
6678
/// This method serializes the filled node into a byte vector, where:
6779
/// - For binary nodes: Concatenates left and right hashes.
6880
/// - For edge nodes: Concatenates bottom hash, path, and path length.
@@ -94,22 +106,21 @@ impl<L: Leaf> DBObject for FilledNode<L> {
94106
NodeData::Leaf(leaf_data) => leaf_data.serialize(),
95107
}
96108
}
97-
}
98109

99-
impl<L: Leaf> FilledNode<L> {
100-
/// Deserializes filled nodes.
101-
pub fn deserialize(
102-
node_hash: HashOutput,
110+
fn deserialize(
103111
value: &DbValue,
104-
is_leaf: bool,
112+
deserialize_context: &Self::DeserializeContext,
105113
) -> Result<Self, DeserializationError> {
106-
if is_leaf {
107-
return Ok(Self { hash: node_hash, data: NodeData::Leaf(L::deserialize(value)?) });
114+
if deserialize_context.is_leaf {
115+
return Ok(Self {
116+
hash: deserialize_context.node_hash,
117+
data: NodeData::Leaf(L::deserialize(value, &EmptyDeserializationContext)?),
118+
});
108119
}
109120

110121
if value.0.len() == BINARY_BYTES {
111122
Ok(Self {
112-
hash: node_hash,
123+
hash: deserialize_context.node_hash,
113124
data: NodeData::Binary(BinaryData {
114125
left_hash: HashOutput(Felt::from_bytes_be_slice(
115126
&value.0[..SERIALIZE_HASH_BYTES],
@@ -129,7 +140,7 @@ impl<L: Leaf> FilledNode<L> {
129140
BINARY_BYTES
130141
);
131142
Ok(Self {
132-
hash: node_hash,
143+
hash: deserialize_context.node_hash,
133144
data: NodeData::Edge(EdgeData {
134145
bottom_hash: HashOutput(Felt::from_bytes_be_slice(
135146
&value.0[..SERIALIZE_HASH_BYTES],

crates/starknet_patricia/src/patricia_merkle_tree/node_data/leaf.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use std::collections::HashMap;
22
use std::fmt::Debug;
33
use std::future::Future;
44

5-
use starknet_patricia_storage::db_object::{DBObject, Deserializable, HasStaticPrefix};
5+
use starknet_patricia_storage::db_object::{
6+
DBObject,
7+
EmptyDeserializationContext,
8+
HasStaticPrefix,
9+
};
610
use starknet_types_core::felt::Felt;
711

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

1317
pub trait Leaf:
14-
Clone + Sync + Send + HasStaticPrefix + DBObject + Deserializable + Default + Debug + Eq
18+
Clone
19+
+ Sync
20+
+ Send
21+
+ HasStaticPrefix
22+
+ DBObject<DeserializeContext = EmptyDeserializationContext>
23+
+ Default
24+
+ Debug
25+
+ Eq
1526
{
1627
// TODO(Amos, 1/1/2025): When default values for associated types are stable - use them, and
1728
// add a default implementation for `create`.

crates/starknet_patricia_storage/src/db_object.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,22 @@ impl<T: HasStaticPrefix> HasDynamicPrefix for T {
3030
}
3131
}
3232

33-
pub trait DBObject: HasDynamicPrefix {
34-
/// Serializes the given value.
33+
pub struct EmptyDeserializationContext;
34+
35+
pub trait DBObject: Sized + HasDynamicPrefix {
36+
/// Extra data needed to deserialize the object. For example, facts layout nodes need the node
37+
/// hash and an indication of whether or not it's a leaf node (index layout nodes only need the
38+
/// latter).
39+
type DeserializeContext;
40+
3541
fn serialize(&self) -> DbValue;
3642

37-
/// Returns a `DbKey` from a prefix and a suffix.
43+
fn deserialize(
44+
value: &DbValue,
45+
deserialize_context: &Self::DeserializeContext,
46+
) -> Result<Self, DeserializationError>;
47+
3848
fn get_db_key(&self, key_context: &Self::KeyContext, suffix: &[u8]) -> DbKey {
3949
create_db_key(self.get_prefix(key_context), suffix)
4050
}
4151
}
42-
43-
pub trait Deserializable: Sized {
44-
/// Deserializes the given value.
45-
fn deserialize(value: &DbValue) -> Result<Self, DeserializationError>;
46-
}

0 commit comments

Comments
 (0)