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