miden_air/
proof.rs

1use alloc::{string::ToString, vec::Vec};
2
3use miden_core::{
4    crypto::hash::{Blake3_192, Blake3_256, Hasher, Poseidon2, 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            HashFunction::Poseidon2 => self.proof.conjectured_security::<Poseidon2>(),
53        };
54        conjectured_security.bits()
55    }
56
57    // SERIALIZATION / DESERIALIZATION
58    // --------------------------------------------------------------------------------------------
59
60    /// Serializes this proof into a vector of bytes.
61    pub fn to_bytes(&self) -> Vec<u8> {
62        let mut bytes = self.proof.to_bytes();
63        assert!(!bytes.is_empty(), "invalid STARK proof");
64        // TODO: ideally we should write hash function into the proof first to avoid reallocations
65        bytes.insert(0, self.hash_fn as u8);
66        bytes
67    }
68
69    /// Reads the source bytes, parsing a new proof instance.
70    pub fn from_bytes(source: &[u8]) -> Result<Self, DeserializationError> {
71        if source.len() < 2 {
72            return Err(DeserializationError::UnexpectedEOF);
73        }
74        let hash_fn = HashFunction::try_from(source[0])?;
75        let proof = Proof::from_bytes(&source[1..])?;
76        Ok(Self::new(proof, hash_fn))
77    }
78
79    // DESTRUCTOR
80    // --------------------------------------------------------------------------------------------
81
82    /// Returns components of this execution proof.
83    pub fn into_parts(self) -> (HashFunction, Proof) {
84        (self.hash_fn, self.proof)
85    }
86}
87
88// HASH FUNCTION
89// ================================================================================================
90
91/// A hash function used during STARK proof generation.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93#[repr(u8)]
94pub enum HashFunction {
95    /// BLAKE3 hash function with 192-bit output.
96    Blake3_192 = 0x00,
97    /// BLAKE3 hash function with 256-bit output.
98    Blake3_256 = 0x01,
99    /// RPO hash function with 256-bit output.
100    Rpo256 = 0x02,
101    /// RPX hash function with 256-bit output.
102    Rpx256 = 0x03,
103    /// Poseidon2 hash function with 256-bit output.
104    Poseidon2 = 0x04,
105}
106
107impl HashFunction {
108    /// Returns the collision resistance level (in bits) of this hash function.
109    pub const fn collision_resistance(&self) -> u32 {
110        match self {
111            HashFunction::Blake3_192 => Blake3_192::COLLISION_RESISTANCE,
112            HashFunction::Blake3_256 => Blake3_256::COLLISION_RESISTANCE,
113            HashFunction::Rpo256 => Rpo256::COLLISION_RESISTANCE,
114            HashFunction::Rpx256 => Rpx256::COLLISION_RESISTANCE,
115            HashFunction::Poseidon2 => Poseidon2::COLLISION_RESISTANCE,
116        }
117    }
118}
119
120impl TryFrom<u8> for HashFunction {
121    type Error = DeserializationError;
122
123    fn try_from(repr: u8) -> Result<Self, Self::Error> {
124        match repr {
125            0x00 => Ok(Self::Blake3_192),
126            0x01 => Ok(Self::Blake3_256),
127            0x02 => Ok(Self::Rpo256),
128            0x03 => Ok(Self::Rpx256),
129            0x04 => Ok(Self::Poseidon2),
130            _ => Err(DeserializationError::InvalidValue(format!(
131                "the hash function representation {repr} is not valid!"
132            ))),
133        }
134    }
135}
136
137impl TryFrom<&str> for HashFunction {
138    type Error = super::ExecutionOptionsError;
139
140    fn try_from(hash_fn_str: &str) -> Result<Self, Self::Error> {
141        match hash_fn_str {
142            "blake3-192" => Ok(Self::Blake3_192),
143            "blake3-256" => Ok(Self::Blake3_256),
144            "rpo" => Ok(Self::Rpo256),
145            "rpx" => Ok(Self::Rpx256),
146            "poseidon2" => Ok(Self::Poseidon2),
147            _ => Err(super::ExecutionOptionsError::InvalidHashFunction {
148                hash_function: hash_fn_str.to_string(),
149            }),
150        }
151    }
152}
153
154// SERIALIZATION
155// ================================================================================================
156
157impl Serializable for HashFunction {
158    fn write_into<W: ByteWriter>(&self, target: &mut W) {
159        target.write_u8(*self as u8);
160    }
161}
162
163impl Deserializable for HashFunction {
164    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
165        source.read_u8()?.try_into()
166    }
167}
168
169impl Serializable for ExecutionProof {
170    fn write_into<W: ByteWriter>(&self, target: &mut W) {
171        self.proof.write_into(target);
172        self.hash_fn.write_into(target);
173    }
174}
175
176impl Deserializable for ExecutionProof {
177    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
178        let proof = Proof::read_from(source)?;
179        let hash_fn = HashFunction::read_from(source)?;
180
181        Ok(ExecutionProof { proof, hash_fn })
182    }
183}
184
185// TESTING UTILS
186// ================================================================================================
187
188#[cfg(any(test, feature = "testing"))]
189impl ExecutionProof {
190    /// Creates a dummy `ExecutionProof` for testing purposes only.
191    ///
192    /// Uses a dummy `Proof` and the default `HashFunction`.
193    pub fn new_dummy() -> Self {
194        ExecutionProof {
195            proof: Proof::new_dummy(),
196            hash_fn: HashFunction::Blake3_192,
197        }
198    }
199}