miden_air/
proof.rs

1use alloc::vec::Vec;
2
3use miden_core::{
4    crypto::hash::{Blake3_192, Blake3_256, Hasher, Rpo256, Rpx256},
5    utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
6};
7use winter_air::proof::Proof;
8
9// EXECUTION PROOF
10// ================================================================================================
11
12/// A proof of correct execution of Miden VM.
13///
14/// The proof encodes the proof itself as well as STARK protocol parameters used to generate the
15/// proof. However, the proof does not contain public inputs needed to verify the proof.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ExecutionProof {
18    pub proof: Proof,
19    pub hash_fn: HashFunction,
20}
21
22impl ExecutionProof {
23    // CONSTRUCTOR
24    // --------------------------------------------------------------------------------------------
25
26    /// Creates a new instance of [ExecutionProof] from the specified STARK proof and hash
27    /// function.
28    pub const fn new(proof: Proof, hash_fn: HashFunction) -> Self {
29        Self { proof, hash_fn }
30    }
31
32    // PUBLIC ACCESSORS
33    // --------------------------------------------------------------------------------------------
34
35    /// Returns the underlying STARK proof.
36    pub const fn stark_proof(&self) -> &Proof {
37        &self.proof
38    }
39
40    /// Returns the hash function used during proof generation process.
41    pub const fn hash_fn(&self) -> HashFunction {
42        self.hash_fn
43    }
44
45    /// Returns conjectured security level of this proof in bits.
46    pub fn security_level(&self) -> u32 {
47        let conjectured_security = match self.hash_fn {
48            HashFunction::Blake3_192 => self.proof.conjectured_security::<Blake3_192>(),
49            HashFunction::Blake3_256 => self.proof.conjectured_security::<Blake3_256>(),
50            HashFunction::Rpo256 => self.proof.conjectured_security::<Rpo256>(),
51            HashFunction::Rpx256 => self.proof.conjectured_security::<Rpx256>(),
52        };
53        conjectured_security.bits()
54    }
55
56    // SERIALIZATION / DESERIALIZATION
57    // --------------------------------------------------------------------------------------------
58
59    /// Serializes this proof into a vector of bytes.
60    pub fn to_bytes(&self) -> Vec<u8> {
61        let mut bytes = self.proof.to_bytes();
62        assert!(!bytes.is_empty(), "invalid STARK proof");
63        // TODO: ideally we should write hash function into the proof first to avoid reallocations
64        bytes.insert(0, self.hash_fn as u8);
65        bytes
66    }
67
68    /// Reads the source bytes, parsing a new proof instance.
69    pub fn from_bytes(source: &[u8]) -> Result<Self, DeserializationError> {
70        if source.len() < 2 {
71            return Err(DeserializationError::UnexpectedEOF);
72        }
73        let hash_fn = HashFunction::try_from(source[0])?;
74        let proof = Proof::from_bytes(&source[1..])?;
75        Ok(Self::new(proof, hash_fn))
76    }
77
78    // DESTRUCTOR
79    // --------------------------------------------------------------------------------------------
80
81    /// Returns components of this execution proof.
82    pub fn into_parts(self) -> (HashFunction, Proof) {
83        (self.hash_fn, self.proof)
84    }
85}
86
87// HASH FUNCTION
88// ================================================================================================
89
90/// A hash function used during STARK proof generation.
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92#[repr(u8)]
93pub enum HashFunction {
94    /// BLAKE3 hash function with 192-bit output.
95    Blake3_192 = 0x00,
96    /// BLAKE3 hash function with 256-bit output.
97    Blake3_256 = 0x01,
98    /// RPO hash function with 256-bit output.
99    Rpo256 = 0x02,
100    /// RPX hash function with 256-bit output.
101    Rpx256 = 0x03,
102}
103
104impl Default for HashFunction {
105    fn default() -> Self {
106        Self::Blake3_192
107    }
108}
109
110impl HashFunction {
111    /// Returns the collision resistance level (in bits) of this hash function.
112    pub const fn collision_resistance(&self) -> u32 {
113        match self {
114            HashFunction::Blake3_192 => Blake3_192::COLLISION_RESISTANCE,
115            HashFunction::Blake3_256 => Blake3_256::COLLISION_RESISTANCE,
116            HashFunction::Rpo256 => Rpo256::COLLISION_RESISTANCE,
117            HashFunction::Rpx256 => Rpx256::COLLISION_RESISTANCE,
118        }
119    }
120}
121
122impl TryFrom<u8> for HashFunction {
123    type Error = DeserializationError;
124
125    fn try_from(repr: u8) -> Result<Self, Self::Error> {
126        match repr {
127            0x00 => Ok(Self::Blake3_192),
128            0x01 => Ok(Self::Blake3_256),
129            0x02 => Ok(Self::Rpo256),
130            0x03 => Ok(Self::Rpx256),
131            _ => Err(DeserializationError::InvalidValue(format!(
132                "the hash function representation {repr} is not valid!"
133            ))),
134        }
135    }
136}
137
138// SERIALIZATION
139// ================================================================================================
140
141impl Serializable for HashFunction {
142    fn write_into<W: ByteWriter>(&self, target: &mut W) {
143        target.write_u8(*self as u8);
144    }
145}
146
147impl Deserializable for HashFunction {
148    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
149        source.read_u8()?.try_into()
150    }
151}
152
153impl Serializable for ExecutionProof {
154    fn write_into<W: ByteWriter>(&self, target: &mut W) {
155        self.proof.write_into(target);
156        self.hash_fn.write_into(target);
157    }
158}
159
160impl Deserializable for ExecutionProof {
161    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
162        let proof = Proof::read_from(source)?;
163        let hash_fn = HashFunction::read_from(source)?;
164
165        Ok(ExecutionProof { proof, hash_fn })
166    }
167}
168
169// TESTING UTILS
170// ================================================================================================
171
172#[cfg(any(test, feature = "testing"))]
173impl ExecutionProof {
174    /// Creates a dummy `ExecutionProof` for testing purposes only.
175    ///
176    /// Uses a dummy `Proof` and the default `HashFunction`.
177    pub fn new_dummy() -> Self {
178        ExecutionProof {
179            proof: Proof::new_dummy(),
180            hash_fn: HashFunction::default(),
181        }
182    }
183}