Skip to content

Commit f0e3f55

Browse files
committed
starknet_committer: define StarknetForestProofs serde
1 parent 19be0db commit f0e3f55

1 file changed

Lines changed: 240 additions & 1 deletion

File tree

  • crates/starknet_committer/src/patricia_merkle_tree

crates/starknet_committer/src/patricia_merkle_tree/types.rs

Lines changed: 240 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;
@@ -36,29 +62,242 @@ pub type ClassesTrie = FilledTreeImpl<CompiledClassHash>;
3662
pub type ContractsTrie = FilledTreeImpl<ContractState>;
3763
pub type StorageTrieMap = HashMap<ContractAddress, StorageTrie>;
3864

65+
#[derive(Clone)]
3966
pub struct ContractsTrieProof {
4067
pub nodes: PreimageMap,
4168
pub leaves: HashMap<ContractAddress, ContractState>,
4269
}
4370

71+
#[derive(Clone)]
4472
pub struct StarknetForestProofs {
4573
pub classes_trie_proof: PreimageMap,
4674
pub contracts_trie_proof: ContractsTrieProof,
4775
pub contracts_trie_storage_proofs: HashMap<ContractAddress, PreimageMap>,
4876
}
4977

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

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

0 commit comments

Comments
 (0)