1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5
6#[cfg(feature = "arbitrary")]
7use proptest::prelude::*;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 crypto::hash::{Blake3_256, Poseidon2, Rpo256, Rpx256},
13 deferred::{DeferredRoot, DeferredStateWire},
14 serde::{
15 BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
16 SliceReader,
17 },
18};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(
27 all(feature = "arbitrary", test),
28 miden_test_serde_macros::serde_test(binary_serde(true))
29)]
30#[repr(u8)]
31pub enum HashFunction {
32 Blake3_256 = 0x01,
34 Rpo256 = 0x02,
36 Rpx256 = 0x03,
38 Poseidon2 = 0x04,
40 Keccak = 0x05,
42}
43
44impl HashFunction {
45 pub const fn collision_resistance(&self) -> u32 {
47 match self {
48 HashFunction::Blake3_256 => Blake3_256::COLLISION_RESISTANCE,
49 HashFunction::Rpo256 => Rpo256::COLLISION_RESISTANCE,
50 HashFunction::Rpx256 => Rpx256::COLLISION_RESISTANCE,
51 HashFunction::Poseidon2 => Poseidon2::COLLISION_RESISTANCE,
52 HashFunction::Keccak => 128,
53 }
54 }
55}
56
57#[derive(Debug, thiserror::Error)]
59#[error(
60 "invalid hash function '{hash_function}'. Valid options are: blake3-256, rpo, rpx, poseidon2, keccak"
61)]
62pub struct InvalidHashFunctionError {
63 pub hash_function: String,
64}
65
66impl TryFrom<u8> for HashFunction {
67 type Error = DeserializationError;
68
69 fn try_from(repr: u8) -> Result<Self, Self::Error> {
70 match repr {
71 0x01 => Ok(Self::Blake3_256),
72 0x02 => Ok(Self::Rpo256),
73 0x03 => Ok(Self::Rpx256),
74 0x04 => Ok(Self::Poseidon2),
75 0x05 => Ok(Self::Keccak),
76 _ => Err(DeserializationError::InvalidValue(format!(
77 "the hash function representation {repr} is not valid!"
78 ))),
79 }
80 }
81}
82
83impl TryFrom<&str> for HashFunction {
84 type Error = InvalidHashFunctionError;
85
86 fn try_from(hash_fn_str: &str) -> Result<Self, Self::Error> {
87 match hash_fn_str {
88 "blake3-256" => Ok(Self::Blake3_256),
89 "rpo" => Ok(Self::Rpo256),
90 "rpx" => Ok(Self::Rpx256),
91 "poseidon2" => Ok(Self::Poseidon2),
92 "keccak" => Ok(Self::Keccak),
93 _ => Err(InvalidHashFunctionError { hash_function: hash_fn_str.to_string() }),
94 }
95 }
96}
97
98#[cfg(feature = "arbitrary")]
99impl Arbitrary for HashFunction {
100 type Parameters = ();
101 type Strategy = BoxedStrategy<Self>;
102
103 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
104 any::<u8>()
105 .prop_map(|tag| match tag % 5 {
106 0 => Self::Blake3_256,
107 1 => Self::Rpo256,
108 2 => Self::Rpx256,
109 3 => Self::Poseidon2,
110 _ => Self::Keccak,
111 })
112 .boxed()
113 }
114}
115
116impl Serializable for HashFunction {
117 fn write_into<W: ByteWriter>(&self, target: &mut W) {
118 target.write_u8(*self as u8);
119 }
120}
121
122impl Deserializable for HashFunction {
123 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
124 source.read_u8()?.try_into()
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub struct ExecutionProof {
141 miden: StarkProof,
142 deferred: DeferredProof,
143}
144
145impl ExecutionProof {
146 pub const fn new(miden: StarkProof, deferred: DeferredProof) -> Self {
152 Self { miden, deferred }
153 }
154
155 pub fn from_parts(
158 miden_proof_bytes: Vec<u8>,
159 hash_fn: HashFunction,
160 deferred: impl Into<DeferredProof>,
161 ) -> Self {
162 Self::new(StarkProof::new(miden_proof_bytes, hash_fn), deferred.into())
163 }
164
165 pub const fn miden_proof(&self) -> &StarkProof {
170 &self.miden
171 }
172
173 pub const fn deferred_proof(&self) -> &DeferredProof {
175 &self.deferred
176 }
177
178 pub const fn is_final(&self) -> bool {
183 self.deferred.is_final()
184 }
185
186 pub fn security_level(&self) -> u32 {
195 96
196 }
197
198 pub fn to_bytes(&self) -> Vec<u8> {
203 let mut bytes = Vec::new();
204 self.write_into(&mut bytes);
205 bytes
206 }
207
208 pub fn from_bytes(source: &[u8]) -> Result<Self, DeserializationError> {
212 <Self as Deserializable>::read_from_bytes(source)
213 }
214}
215
216impl Serializable for ExecutionProof {
217 fn write_into<W: ByteWriter>(&self, target: &mut W) {
218 self.miden.write_into(target);
219 self.deferred.write_into(target);
220 }
221}
222
223impl Deserializable for ExecutionProof {
224 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
225 let miden = StarkProof::read_from(source)?;
226 let deferred = DeferredProof::read_from(source)?;
227
228 Ok(ExecutionProof::new(miden, deferred))
229 }
230
231 fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
232 let mut reader = BudgetedReader::new(SliceReader::new(bytes), bytes.len());
233 Self::read_from(&mut reader)
234 }
235}
236
237#[cfg(any(test, feature = "testing"))]
238impl ExecutionProof {
239 pub fn new_dummy() -> Self {
243 ExecutionProof::new(
244 StarkProof::new(Vec::new(), HashFunction::Blake3_256),
245 DeferredProof::Empty,
246 )
247 }
248}
249
250#[derive(Debug, Clone, PartialEq, Eq)]
260#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
261pub enum DeferredProof {
262 Empty,
265 Wire(DeferredStateWire),
267 Stark {
272 proof: StarkProof,
273 public_root: DeferredRoot,
274 },
275}
276
277impl DeferredProof {
278 const EMPTY_TAG: u8 = 0;
279 pub(crate) const WIRE_TAG: u8 = 1;
280 const STARK_TAG: u8 = 2;
281
282 pub const fn empty() -> Self {
284 Self::Empty
285 }
286
287 pub const fn wire(wire: DeferredStateWire) -> Self {
289 Self::Wire(wire)
290 }
291
292 pub const fn stark(proof: StarkProof, public_root: DeferredRoot) -> Self {
294 Self::Stark { proof, public_root }
295 }
296
297 pub const fn is_empty(&self) -> bool {
299 matches!(self, Self::Empty)
300 }
301
302 pub const fn is_final(&self) -> bool {
307 matches!(self, Self::Empty | Self::Stark { .. })
308 }
309
310 pub const fn as_wire(&self) -> Option<&DeferredStateWire> {
312 match self {
313 Self::Wire(wire) => Some(wire),
314 _ => None,
315 }
316 }
317
318 pub const fn as_stark(&self) -> Option<(&StarkProof, DeferredRoot)> {
320 match self {
321 Self::Stark { proof, public_root } => Some((proof, *public_root)),
322 _ => None,
323 }
324 }
325}
326
327impl From<DeferredStateWire> for DeferredProof {
328 fn from(wire: DeferredStateWire) -> Self {
329 Self::wire(wire)
330 }
331}
332
333impl Serializable for DeferredProof {
334 fn write_into<W: ByteWriter>(&self, target: &mut W) {
335 match self {
336 Self::Empty => target.write_u8(Self::EMPTY_TAG),
337 Self::Wire(wire) => {
338 target.write_u8(Self::WIRE_TAG);
339 wire.write_into(target);
340 },
341 Self::Stark { proof, public_root } => {
342 target.write_u8(Self::STARK_TAG);
343 proof.write_into(target);
344 public_root.write_into(target);
345 },
346 }
347 }
348}
349
350impl Deserializable for DeferredProof {
351 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
352 let tag = source.read_u8()?;
353 match tag {
354 Self::EMPTY_TAG => Ok(Self::Empty),
355 Self::WIRE_TAG => Ok(Self::Wire(DeferredStateWire::read_from(source)?)),
356 Self::STARK_TAG => {
357 let proof = StarkProof::read_from(source)?;
358 let public_root = <DeferredRoot as Deserializable>::read_from(source)?;
359 Ok(Self::Stark { proof, public_root })
360 },
361 other => Err(DeserializationError::InvalidValue(format!(
362 "invalid deferred proof discriminant: {other}"
363 ))),
364 }
365 }
366
367 fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
368 let mut reader = BudgetedReader::new(SliceReader::new(bytes), bytes.len());
369 Self::read_from(&mut reader)
370 }
371
372 fn min_serialized_size() -> usize {
373 1
374 }
375}
376
377#[derive(Debug, Clone, PartialEq, Eq)]
382#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
383pub struct StarkProof {
384 bytes: Vec<u8>,
385 hash_fn: HashFunction,
386}
387
388impl StarkProof {
389 pub const fn new(bytes: Vec<u8>, hash_fn: HashFunction) -> Self {
391 Self { bytes, hash_fn }
392 }
393
394 pub fn bytes(&self) -> &[u8] {
396 &self.bytes
397 }
398
399 pub const fn hash_fn(&self) -> HashFunction {
401 self.hash_fn
402 }
403
404 pub fn into_parts(self) -> (Vec<u8>, HashFunction) {
406 (self.bytes, self.hash_fn)
407 }
408}
409
410impl Serializable for StarkProof {
411 fn write_into<W: ByteWriter>(&self, target: &mut W) {
412 self.bytes.write_into(target);
413 self.hash_fn.write_into(target);
414 }
415}
416
417impl Deserializable for StarkProof {
418 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
419 let bytes = Vec::<u8>::read_from(source)?;
420 let hash_fn = HashFunction::read_from(source)?;
421 Ok(Self::new(bytes, hash_fn))
422 }
423
424 fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
425 let mut reader = BudgetedReader::new(SliceReader::new(bytes), bytes.len());
426 Self::read_from(&mut reader)
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433 use crate::{
434 Felt,
435 deferred::{DeferredRoot, TRUE_INDEX, Tag, WireEntry},
436 serde::{BudgetedReader, ByteWriter, DeserializationError, SliceReader},
437 };
438
439 #[test]
440 fn execution_proof_from_bytes_rejects_unbounded_proof_len() {
441 let mut bytes = Vec::new();
442 bytes.write_usize(usize::MAX);
443
444 let err = ExecutionProof::from_bytes(&bytes).unwrap_err();
445 let DeserializationError::InvalidValue(message) = err else {
446 panic!("expected InvalidValue error");
447 };
448 assert!(message.contains("requested"));
449 assert!(message.contains("reader can provide at most"));
450 }
451
452 #[test]
453 fn execution_proof_read_from_bytes_rejects_unbounded_proof_len() {
454 let mut bytes = Vec::new();
455 bytes.write_usize(usize::MAX);
456
457 let err = ExecutionProof::read_from_bytes(&bytes).unwrap_err();
458 let DeserializationError::InvalidValue(message) = err else {
459 panic!("expected InvalidValue error");
460 };
461 assert!(message.contains("requested"));
462 assert!(message.contains("reader can provide at most"));
463 }
464
465 #[test]
466 fn execution_proof_round_trips_empty_deferred_proof() {
467 let proof = ExecutionProof::new(
468 StarkProof::new(alloc::vec![1, 2, 3], HashFunction::Blake3_256),
469 DeferredProof::empty(),
470 );
471
472 let decoded = ExecutionProof::from_bytes(&proof.to_bytes()).unwrap();
473
474 assert_eq!(decoded, proof);
475 assert_eq!(decoded.miden_proof().bytes(), &[1, 2, 3]);
476 assert_eq!(decoded.miden_proof().hash_fn(), HashFunction::Blake3_256);
477 assert!(decoded.deferred_proof().is_empty());
478 assert!(decoded.is_final());
479 }
480
481 #[test]
482 fn execution_proof_round_trips_empty_deferred_wire() {
483 let proof = ExecutionProof::from_parts(
484 alloc::vec![1, 2, 3],
485 HashFunction::Blake3_256,
486 DeferredProof::wire(DeferredStateWire::default()),
487 );
488
489 let decoded = ExecutionProof::from_bytes(&proof.to_bytes()).unwrap();
490
491 assert_eq!(decoded, proof);
492 assert_eq!(decoded.deferred_proof().as_wire(), Some(&DeferredStateWire::default()));
493 assert!(!decoded.is_final());
494 }
495
496 #[test]
497 fn execution_proof_round_trips_non_empty_deferred_wire() {
498 let tag = Tag::from_word([
499 Felt::new_unchecked(7),
500 Felt::new_unchecked(1),
501 Felt::new_unchecked(2),
502 Felt::new_unchecked(3),
503 ]);
504 let deferred_wire = DeferredStateWire {
505 entries: alloc::vec![
506 WireEntry::Data {
507 tag,
508 chunks: alloc::vec![[Felt::new_unchecked(1); 8]],
509 },
510 WireEntry::Join { tag, lhs: TRUE_INDEX, rhs: 1 },
511 ],
512 };
513 let proof = ExecutionProof::from_parts(
514 alloc::vec![1, 2, 3],
515 HashFunction::Blake3_256,
516 deferred_wire,
517 );
518
519 let decoded = ExecutionProof::from_bytes(&proof.to_bytes()).unwrap();
520
521 assert_eq!(decoded, proof);
522 assert!(!decoded.is_final());
523 }
524
525 #[test]
526 fn execution_proof_round_trips_stark_deferred_proof() {
527 let public_root: DeferredRoot = [
528 Felt::new_unchecked(9),
529 Felt::new_unchecked(8),
530 Felt::new_unchecked(7),
531 Felt::new_unchecked(6),
532 ]
533 .into();
534 let deferred_stark_proof = StarkProof::new(alloc::vec![4, 5, 6], HashFunction::Poseidon2);
535 let deferred = DeferredProof::stark(deferred_stark_proof.clone(), public_root);
536 let proof = ExecutionProof::new(
537 StarkProof::new(alloc::vec![1, 2, 3], HashFunction::Blake3_256),
538 deferred,
539 );
540
541 let decoded = ExecutionProof::from_bytes(&proof.to_bytes()).unwrap();
542
543 assert_eq!(decoded, proof);
544 assert_eq!(decoded.deferred_proof().as_stark(), Some((&deferred_stark_proof, public_root)));
545 assert!(decoded.is_final());
546 }
547
548 #[test]
549 fn execution_proof_rejects_invalid_deferred_variant() {
550 let mut bytes = Vec::new();
551 bytes.write_usize(0);
552 bytes.write_u8(HashFunction::Blake3_256 as u8);
553 bytes.write_u8(255);
554
555 let err = ExecutionProof::from_bytes(&bytes).unwrap_err();
556 let DeserializationError::InvalidValue(message) = err else {
557 panic!("expected InvalidValue error");
558 };
559 assert!(message.contains("invalid deferred proof discriminant: 255"));
560 }
561
562 #[test]
563 fn execution_proof_rejects_over_budget_proof_len() {
564 let mut bytes = Vec::new();
565 bytes.write_usize(5);
566
567 let budget = bytes.len() + 4;
568 let mut reader = BudgetedReader::new(SliceReader::new(&bytes), budget);
569 let err = ExecutionProof::read_from(&mut reader).unwrap_err();
570 let DeserializationError::InvalidValue(message) = err else {
571 panic!("expected InvalidValue error");
572 };
573 assert!(message.contains("requested 5 elements"));
574 }
575
576 #[test]
577 fn execution_proof_rejects_over_budget_deferred_wire_entries_len() {
578 let mut bytes = Vec::new();
579 bytes.write_usize(0);
580 bytes.write_u8(HashFunction::Blake3_256 as u8);
581 bytes.write_u8(DeferredProof::WIRE_TAG);
582 bytes.write_usize(2);
583
584 let budget = bytes.len() + 1;
585 let mut reader = BudgetedReader::new(SliceReader::new(&bytes), budget);
586 let err = ExecutionProof::read_from(&mut reader).unwrap_err();
587 let DeserializationError::InvalidValue(message) = err else {
588 panic!("expected InvalidValue error");
589 };
590 assert!(message.contains("requested 2 elements"));
591 }
592}