Skip to content

Commit 40a2552

Browse files
committed
starknet_committer: define StarknetForestProofs serde
1 parent 8149407 commit 40a2552

1 file changed

Lines changed: 234 additions & 1 deletion

File tree

  • crates/starknet_committer/src/patricia_merkle_tree

crates/starknet_committer/src/patricia_merkle_tree/types.rs

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
11
use std::collections::HashMap;
22

3+
#[cfg(feature = "os_input")]
4+
use ethnum::U256;
35
use serde::{Deserialize, Serialize};
6+
#[cfg(feature = "os_input")]
7+
use starknet_api::core::Nonce;
48
use starknet_api::core::{ClassHash, ContractAddress};
59
use starknet_api::hash::HashOutput;
610
use starknet_patricia::impl_from_hex_for_felt_wrapper;
711
use starknet_patricia::patricia_merkle_tree::filled_tree::tree::FilledTreeImpl;
12+
#[cfg(feature = "os_input")]
13+
use starknet_patricia::patricia_merkle_tree::node_data::errors::{
14+
EdgePathError,
15+
PathToBottomError,
16+
};
817
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::PreimageMap;
18+
#[cfg(feature = "os_input")]
19+
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
20+
BinaryData,
21+
EdgeData,
22+
EdgePath,
23+
EdgePathLength,
24+
PathToBottom,
25+
Preimage,
26+
};
927
use starknet_patricia::patricia_merkle_tree::types::NodeIndex;
28+
#[cfg(feature = "os_input")]
29+
use starknet_patricia_storage::errors::{
30+
DeserializationError,
31+
SerializationError,
32+
SerializationResult,
33+
};
34+
#[cfg(feature = "os_input")]
35+
use starknet_patricia_storage::storage_trait::DbValue;
1036
use starknet_types_core::felt::{Felt, FromStrError};
1137

1238
use crate::block_committer::input::StarknetStorageValue;
@@ -48,17 +74,224 @@ pub struct StarknetForestProofs {
4874
}
4975

5076
impl StarknetForestProofs {
51-
pub(crate) fn extend(&mut self, other: Self) {
77+
pub fn extend(&mut self, other: Self) {
5278
self.classes_trie_proof.extend(other.classes_trie_proof);
5379
self.contracts_trie_proof.nodes.extend(other.contracts_trie_proof.nodes);
5480
self.contracts_trie_proof.leaves.extend(other.contracts_trie_proof.leaves);
5581
for (address, proof) in other.contracts_trie_storage_proofs {
5682
self.contracts_trie_storage_proofs.entry(address).or_default().extend(proof);
5783
}
5884
}
85+
86+
/// Bincode payload for the OS-input witness KV (structured proofs, round-trips with
87+
/// [`Self::deserialize`]).
88+
#[cfg(feature = "os_input")]
89+
pub fn serialize(&self) -> SerializationResult<DbValue> {
90+
let classes = sorted_encoded_preimage_map(&self.classes_trie_proof)?;
91+
let contract_nodes = sorted_encoded_preimage_map(&self.contracts_trie_proof.nodes)?;
92+
let mut contract_leaves: Vec<(ContractAddress, (Nonce, HashOutput, ClassHash))> = self
93+
.contracts_trie_proof
94+
.leaves
95+
.iter()
96+
.map(|(addr, s)| (*addr, (s.nonce, s.storage_root_hash, s.class_hash)))
97+
.collect();
98+
contract_leaves.sort_by(|(a, _), (b, _)| a.cmp(b));
99+
100+
let mut storage: Vec<(ContractAddress, Vec<(HashOutput, Vec<u8>)>)> = Vec::new();
101+
for (addr, m) in &self.contracts_trie_storage_proofs {
102+
storage.push((*addr, sorted_encoded_preimage_map(m)?));
103+
}
104+
storage.sort_by(|(a, _), (b, _)| a.cmp(b));
105+
106+
bincode::serialize(&(classes, contract_nodes, contract_leaves, storage))
107+
.map(DbValue)
108+
.map_err(bincode_ser_err)
109+
}
110+
111+
#[cfg(feature = "os_input")]
112+
pub fn deserialize(value: &DbValue) -> Result<Self, DeserializationError> {
113+
let (classes, contract_nodes, contract_leaves, storage): (
114+
Vec<(HashOutput, Vec<u8>)>,
115+
Vec<(HashOutput, Vec<u8>)>,
116+
Vec<(ContractAddress, (Nonce, HashOutput, ClassHash))>,
117+
Vec<(ContractAddress, Vec<(HashOutput, Vec<u8>)>)>,
118+
) = bincode::deserialize(&value.0).map_err(|e| {
119+
DeserializationError::ValueError(Box::new(std::io::Error::new(
120+
std::io::ErrorKind::InvalidData,
121+
e.to_string(),
122+
)))
123+
})?;
124+
125+
let classes_trie_proof = preimage_map_from_encoded(classes)?;
126+
let contracts_trie_proof = ContractsTrieProof {
127+
nodes: preimage_map_from_encoded(contract_nodes)?,
128+
leaves: contract_leaves.into_iter().try_fold(
129+
HashMap::new(),
130+
|mut m, (addr, (nonce, storage_root_hash, class_hash))| {
131+
if m.insert(addr, ContractState { nonce, storage_root_hash, class_hash })
132+
.is_some()
133+
{
134+
return Err(DeserializationError::KeyDuplicate(format!(
135+
"duplicate contracts trie leaf {addr:?}"
136+
)));
137+
}
138+
Ok(m)
139+
},
140+
)?,
141+
};
142+
143+
let mut contracts_trie_storage_proofs = HashMap::new();
144+
for (addr, entries) in storage {
145+
if contracts_trie_storage_proofs
146+
.insert(addr, preimage_map_from_encoded(entries)?)
147+
.is_some()
148+
{
149+
return Err(DeserializationError::KeyDuplicate(format!(
150+
"duplicate storage trie witness key {addr:?}"
151+
)));
152+
}
153+
}
154+
155+
Ok(Self { classes_trie_proof, contracts_trie_proof, contracts_trie_storage_proofs })
156+
}
157+
}
158+
159+
#[cfg(feature = "os_input")]
160+
impl serde::Serialize for StarknetForestProofs {
161+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162+
where
163+
S: serde::Serializer,
164+
{
165+
let encoded = Self::serialize(self).map_err(serde::ser::Error::custom)?;
166+
serializer.serialize_bytes(&encoded.0)
167+
}
168+
}
169+
170+
#[cfg(feature = "os_input")]
171+
impl<'de> serde::Deserialize<'de> for StarknetForestProofs {
172+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173+
where
174+
D: serde::Deserializer<'de>,
175+
{
176+
use serde::de::Error;
177+
let bytes = <Vec<u8>>::deserialize(deserializer)?;
178+
Self::deserialize(&DbValue(bytes)).map_err(Error::custom)
179+
}
59180
}
60181

61182
pub struct RootHashes {
62183
pub previous_root_hash: HashOutput,
63184
pub new_root_hash: HashOutput,
64185
}
186+
187+
#[cfg(feature = "os_input")]
188+
const WITNESS_PREIMAGE_BINARY: u8 = 0;
189+
#[cfg(feature = "os_input")]
190+
const WITNESS_PREIMAGE_EDGE: u8 = 1;
191+
192+
#[cfg(feature = "os_input")]
193+
fn bincode_ser_err(e: bincode::Error) -> SerializationError {
194+
SerializationError::IOSerialize(std::io::Error::other(e))
195+
}
196+
197+
#[cfg(feature = "os_input")]
198+
fn encode_preimage(p: &Preimage) -> Result<Vec<u8>, SerializationError> {
199+
match p {
200+
Preimage::Binary(b) => {
201+
let payload =
202+
bincode::serialize(&(b.left_data, b.right_data)).map_err(bincode_ser_err)?;
203+
let mut out = Vec::with_capacity(1 + payload.len());
204+
out.push(WITNESS_PREIMAGE_BINARY);
205+
out.extend_from_slice(&payload);
206+
Ok(out)
207+
}
208+
Preimage::Edge(e) => {
209+
let path_bytes = e.path_to_bottom.path.0.to_be_bytes();
210+
let payload =
211+
bincode::serialize(&(e.bottom_data, path_bytes, u8::from(e.path_to_bottom.length)))
212+
.map_err(bincode_ser_err)?;
213+
let mut out = Vec::with_capacity(1 + payload.len());
214+
out.push(WITNESS_PREIMAGE_EDGE);
215+
out.extend_from_slice(&payload);
216+
Ok(out)
217+
}
218+
}
219+
}
220+
221+
#[cfg(feature = "os_input")]
222+
fn decode_preimage(encoded: &[u8]) -> Result<Preimage, DeserializationError> {
223+
let Some((&tag, payload)) = encoded.split_first() else {
224+
return Err(DeserializationError::ValueError(Box::new(std::io::Error::new(
225+
std::io::ErrorKind::InvalidData,
226+
"empty encoded preimage",
227+
))));
228+
};
229+
match tag {
230+
WITNESS_PREIMAGE_BINARY => {
231+
let (left, right): (HashOutput, HashOutput) =
232+
bincode::deserialize(payload).map_err(|e| {
233+
DeserializationError::ValueError(Box::new(std::io::Error::new(
234+
std::io::ErrorKind::InvalidData,
235+
e.to_string(),
236+
)))
237+
})?;
238+
Ok(Preimage::Binary(BinaryData { left_data: left, right_data: right }))
239+
}
240+
WITNESS_PREIMAGE_EDGE => {
241+
let (bottom, path_bytes, length_u8): (HashOutput, [u8; 32], u8) =
242+
bincode::deserialize(payload).map_err(|e| {
243+
DeserializationError::ValueError(Box::new(std::io::Error::new(
244+
std::io::ErrorKind::InvalidData,
245+
e.to_string(),
246+
)))
247+
})?;
248+
let path = EdgePath(U256::from_be_bytes(path_bytes));
249+
let length = EdgePathLength::new(length_u8).map_err(|e: EdgePathError| {
250+
DeserializationError::ValueError(Box::new(std::io::Error::new(
251+
std::io::ErrorKind::InvalidData,
252+
e.to_string(),
253+
)))
254+
})?;
255+
let path_to_bottom =
256+
PathToBottom::new(path, length).map_err(|e: PathToBottomError| {
257+
DeserializationError::ValueError(Box::new(std::io::Error::new(
258+
std::io::ErrorKind::InvalidData,
259+
e.to_string(),
260+
)))
261+
})?;
262+
Ok(Preimage::Edge(EdgeData { bottom_data: bottom, path_to_bottom }))
263+
}
264+
other => Err(DeserializationError::ValueError(Box::new(std::io::Error::new(
265+
std::io::ErrorKind::InvalidData,
266+
format!("unknown preimage tag {other}"),
267+
)))),
268+
}
269+
}
270+
271+
#[cfg(feature = "os_input")]
272+
fn sorted_encoded_preimage_map(
273+
m: &PreimageMap,
274+
) -> Result<Vec<(HashOutput, Vec<u8>)>, SerializationError> {
275+
let mut v = Vec::with_capacity(m.len());
276+
for (h, p) in m {
277+
let encoded = encode_preimage(p)?;
278+
v.push((*h, encoded));
279+
}
280+
v.sort_by(|(a, _), (b, _)| a.0.cmp(&b.0));
281+
Ok(v)
282+
}
283+
284+
#[cfg(feature = "os_input")]
285+
fn preimage_map_from_encoded(
286+
v: Vec<(HashOutput, Vec<u8>)>,
287+
) -> Result<PreimageMap, DeserializationError> {
288+
let mut m = PreimageMap::new();
289+
for (h, buf) in v {
290+
if m.insert(h, decode_preimage(&buf)?).is_some() {
291+
return Err(DeserializationError::KeyDuplicate(format!(
292+
"duplicate preimage node hash {h:?}"
293+
)));
294+
}
295+
}
296+
Ok(m)
297+
}

0 commit comments

Comments
 (0)