miden_core/mast/serialization/
sparse.rs1use alloc::{collections::BTreeMap, format, string::ToString, vec::Vec};
2
3use super::{
4 TRUSTED_BYTE_READ_BUDGET_MULTIPLIER,
5 basic_blocks::{BasicBlockDataBuilder, BasicBlockDataDecoder},
6};
7use crate::{
8 Word,
9 advice::AdviceMap,
10 mast::{
11 BasicBlockNodeBuilder, CallNodeBuilder, DynNodeBuilder, ExternalNodeBuilder,
12 JoinNodeBuilder, LoopNodeBuilder, MastForest, MastForestContributor, MastNode, MastNodeExt,
13 MastNodeId, SparseMastForest, SplitNodeBuilder,
14 },
15 serde::{
16 BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
17 SliceReader, read_bounded_len,
18 },
19};
20
21const SPARSE_BLOCK: u8 = 0;
22const SPARSE_JOIN: u8 = 1;
23const SPARSE_SPLIT: u8 = 2;
24const SPARSE_LOOP: u8 = 3;
25const SPARSE_CALL: u8 = 4;
26const SPARSE_SYSCALL: u8 = 5;
27const SPARSE_DYN: u8 = 6;
28const SPARSE_DYNCALL: u8 = 7;
29const SPARSE_EXTERNAL: u8 = 8;
30
31fn write_sparse_into<W: ByteWriter>(forest: &SparseMastForest, target: &mut W) {
41 write_node_ids(forest.procedure_roots(), target);
42 write_sparse_nodes(forest.nodes(), target);
43 write_digest_entries(forest.digest_entries(), target);
44 forest.advice_map().write_into(target);
45}
46
47fn write_node_ids<W: ByteWriter>(ids: &[MastNodeId], target: &mut W) {
48 target.write_usize(ids.len());
49 for id in ids {
50 id.write_into(target);
51 }
52}
53
54fn write_sparse_nodes<W: ByteWriter>(nodes: &BTreeMap<MastNodeId, MastNode>, target: &mut W) {
55 target.write_usize(nodes.len());
56 for (&id, node) in nodes {
57 id.write_into(target);
58 write_sparse_node(node, target);
59 }
60}
61
62fn write_digest_entries<W: ByteWriter>(digests: &BTreeMap<MastNodeId, Word>, target: &mut W) {
63 target.write_usize(digests.len());
64 for (&id, &digest) in digests {
65 id.write_into(target);
66 digest.write_into(target);
67 }
68}
69
70fn write_sparse_node<W: ByteWriter>(node: &MastNode, target: &mut W) {
71 match node {
72 MastNode::Block(block) => {
73 target.write_u8(SPARSE_BLOCK);
74 node.digest().write_into(target);
75
76 let mut basic_block_data = BasicBlockDataBuilder::new();
77 let ops_offset = basic_block_data.encode_basic_block(block);
78 debug_assert_eq!(ops_offset, 0);
79 let basic_block_data = basic_block_data.finalize();
80 target.write_usize(basic_block_data.len());
81 target.write_bytes(&basic_block_data);
82 },
83 MastNode::Join(join) => {
84 target.write_u8(SPARSE_JOIN);
85 node.digest().write_into(target);
86 join.first().write_into(target);
87 join.second().write_into(target);
88 },
89 MastNode::Split(split) => {
90 target.write_u8(SPARSE_SPLIT);
91 node.digest().write_into(target);
92 split.on_true().write_into(target);
93 split.on_false().write_into(target);
94 },
95 MastNode::Loop(loop_node) => {
96 target.write_u8(SPARSE_LOOP);
97 node.digest().write_into(target);
98 loop_node.body().write_into(target);
99 },
100 MastNode::Call(call) => {
101 target.write_u8(if call.is_syscall() { SPARSE_SYSCALL } else { SPARSE_CALL });
102 node.digest().write_into(target);
103 call.callee().write_into(target);
104 },
105 MastNode::Dyn(dyn_node) => {
106 target.write_u8(if dyn_node.is_dyncall() {
107 SPARSE_DYNCALL
108 } else {
109 SPARSE_DYN
110 });
111 node.digest().write_into(target);
112 },
113 MastNode::External(_) => {
114 target.write_u8(SPARSE_EXTERNAL);
115 node.digest().write_into(target);
116 },
117 }
118}
119
120impl Serializable for SparseMastForest {
124 fn write_into<W: ByteWriter>(&self, target: &mut W) {
125 write_sparse_into(self, target);
126 }
127}
128
129impl Deserializable for SparseMastForest {
130 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
131 read_sparse_from(source)
132 }
133
134 fn min_serialized_size() -> usize {
135 usize::min_serialized_size()
136 }
137
138 fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
145 let budget = bytes.len().saturating_mul(TRUSTED_BYTE_READ_BUDGET_MULTIPLIER);
146 let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
147 let forest = read_sparse_from(&mut reader)?;
148 if reader.has_more_bytes() {
149 return Err(DeserializationError::InvalidValue(
150 "extra bytes after SparseMastForest payload".to_string(),
151 ));
152 }
153 Ok(forest)
154 }
155}
156
157fn read_sparse_from<R: ByteReader>(
161 source: &mut R,
162) -> Result<SparseMastForest, DeserializationError> {
163 let roots = read_node_ids(source, "procedure root")?;
164 let nodes = read_sparse_nodes(source)?;
165 let digest_entries = read_digest_entries(source)?;
166 let advice_map = read_empty_advice_map(source)?;
167
168 SparseMastForest::from_serialized_parts(nodes, digest_entries, roots, advice_map)
169}
170
171fn read_empty_advice_map<R: ByteReader>(source: &mut R) -> Result<AdviceMap, DeserializationError> {
172 let count = source.read_usize()?;
173 if count != 0 {
174 return Err(DeserializationError::InvalidValue(
175 "sparse MAST replay payload must not carry advice map entries".to_string(),
176 ));
177 }
178
179 Ok(AdviceMap::default())
180}
181
182fn read_node_ids<R: ByteReader>(
183 source: &mut R,
184 label: &str,
185) -> Result<Vec<MastNodeId>, DeserializationError> {
186 let count = read_bounded_count(source, u32::min_serialized_size(), label)?;
187 let mut ids = Vec::with_capacity(count);
188 for _ in 0..count {
189 ids.push(read_node_id(source, label)?);
190 }
191 Ok(ids)
192}
193
194fn read_sparse_nodes<R: ByteReader>(
195 source: &mut R,
196) -> Result<Vec<(MastNodeId, MastNode)>, DeserializationError> {
197 let count = read_bounded_count(source, sparse_node_min_size(), "full node count")?;
198 let mut nodes = Vec::with_capacity(count);
199 let mut previous_id = None;
200
201 for _ in 0..count {
202 let id = read_node_id(source, "full node")?;
203 validate_strictly_increasing_id(previous_id, id, "full node")?;
204 let node = read_sparse_node(source, id)?;
205 nodes.push((id, node));
206 previous_id = Some(id);
207 }
208
209 Ok(nodes)
210}
211
212fn read_digest_entries<R: ByteReader>(
213 source: &mut R,
214) -> Result<Vec<(MastNodeId, Word)>, DeserializationError> {
215 let count =
216 read_bounded_count(source, sparse_digest_entry_min_size(), "digest-only node count")?;
217 let mut digests = Vec::with_capacity(count);
218 let mut previous_id = None;
219
220 for _ in 0..count {
221 let id = read_node_id(source, "digest-only node")?;
222 validate_strictly_increasing_id(previous_id, id, "digest-only node")?;
223 let digest = Word::read_from(source)?;
224 digests.push((id, digest));
225 previous_id = Some(id);
226 }
227
228 Ok(digests)
229}
230
231fn read_sparse_node<R: ByteReader>(
232 source: &mut R,
233 node_id: MastNodeId,
234) -> Result<MastNode, DeserializationError> {
235 let tag = source.read_u8()?;
236 let digest = Word::read_from(source)?;
237
238 let result = match tag {
239 SPARSE_BLOCK => {
240 let len = read_bounded_count(source, 1, "basic block data length")?;
241 let data = source.read_vec(len)?;
242 let decoder = BasicBlockDataDecoder::new(&data);
243 let op_batches = decoder.decode_operations(0)?;
244 BasicBlockNodeBuilder::from_op_batches(op_batches, digest)
245 .build()
246 .map(Into::into)
247 },
248 SPARSE_JOIN => {
249 let first = read_node_id(source, "join first child")?;
250 let second = read_node_id(source, "join second child")?;
251 JoinNodeBuilder::new([first, second])
252 .with_digest(digest)
253 .build_linked()
254 .map(Into::into)
255 },
256 SPARSE_SPLIT => {
257 let on_true = read_node_id(source, "split true child")?;
258 let on_false = read_node_id(source, "split false child")?;
259 SplitNodeBuilder::new([on_true, on_false])
260 .with_digest(digest)
261 .build_linked()
262 .map(Into::into)
263 },
264 SPARSE_LOOP => {
265 let body = read_node_id(source, "loop body")?;
266 LoopNodeBuilder::new(body).with_digest(digest).build_linked().map(Into::into)
267 },
268 SPARSE_CALL | SPARSE_SYSCALL => {
269 let callee = read_node_id(source, "call callee")?;
270 let builder = if tag == SPARSE_SYSCALL {
271 CallNodeBuilder::new_syscall(callee)
272 } else {
273 CallNodeBuilder::new(callee)
274 };
275 builder.with_digest(digest).build_linked().map(Into::into)
276 },
277 SPARSE_DYN | SPARSE_DYNCALL => {
278 let builder = if tag == SPARSE_DYNCALL {
279 DynNodeBuilder::new_dyncall()
280 } else {
281 DynNodeBuilder::new_dyn()
282 };
283 Ok(builder.with_digest(digest).build().into())
284 },
285 SPARSE_EXTERNAL => Ok(ExternalNodeBuilder::new(digest).build().into()),
286 _ => {
287 return Err(DeserializationError::InvalidValue(format!(
288 "invalid sparse MAST node tag {tag}"
289 )));
290 },
291 };
292
293 result.map_err(|err| {
294 DeserializationError::InvalidValue(format!(
295 "failed to build sparse MAST node {}: {}",
296 node_id.0, err
297 ))
298 })
299}
300
301fn read_bounded_count<R: ByteReader>(
302 source: &mut R,
303 element_size: usize,
304 label: &str,
305) -> Result<usize, DeserializationError> {
306 read_bounded_len(source, label, element_size)
307}
308
309fn sparse_node_min_size() -> usize {
310 u32::min_serialized_size() + u8::min_serialized_size() + Word::min_serialized_size()
311}
312
313fn sparse_digest_entry_min_size() -> usize {
314 u32::min_serialized_size() + Word::min_serialized_size()
315}
316
317fn read_node_id<R: ByteReader>(
318 source: &mut R,
319 label: &str,
320) -> Result<MastNodeId, DeserializationError> {
321 let raw = u32::read_from(source)?;
322 MastNodeId::from_u32_with_node_count(raw, MastForest::MAX_NODES).map_err(|err| {
323 DeserializationError::InvalidValue(format!("invalid {label} id {raw}: {err}"))
324 })
325}
326
327fn validate_strictly_increasing_id(
328 previous: Option<MastNodeId>,
329 current: MastNodeId,
330 label: &str,
331) -> Result<(), DeserializationError> {
332 if previous.is_some_and(|previous| previous >= current) {
333 return Err(DeserializationError::InvalidValue(format!(
334 "{label} ids must be strictly increasing"
335 )));
336 }
337 Ok(())
338}