1use arbitrary::Arbitrary;
2use strum::Display;
3use strum::EnumCount;
4use strum::EnumDiscriminants;
5use strum::EnumIter;
6use twenty_first::prelude::*;
7
8use crate::error::ProofStreamError;
9use crate::error::ProofStreamError::UnexpectedItem;
10use crate::fri::AuthenticationStructure;
11use crate::table::AuxiliaryRow;
12use crate::table::MainRow;
13use crate::table::QuotientSegments;
14
15#[derive(Debug, Clone, Eq, PartialEq, Hash, BFieldCodec, Arbitrary)]
19pub struct FriResponse {
20 pub auth_structure: AuthenticationStructure,
22
23 pub revealed_leaves: Vec<XFieldElement>,
25}
26
27macro_rules! proof_items {
28 ($($variant:ident($payload:ty) => $in_fiat_shamir_heuristic:literal, $try_into_fn:ident,)+) => {
29 #[derive(
30 Debug,
31 Display,
32 Clone,
33 Eq,
34 PartialEq,
35 Hash,
36 EnumCount,
37 EnumDiscriminants,
38 BFieldCodec,
39 Arbitrary,
40 )]
41 #[strum_discriminants(name(ProofItemVariant))]
42 #[strum_discriminants(derive(Display, EnumIter, BFieldCodec, Arbitrary))]
44 pub enum ProofItem {
45 $( $variant($payload), )+
46 }
47
48 impl ProofItem {
49 pub const fn include_in_fiat_shamir_heuristic(&self) -> bool {
62 match self {
63 $( Self::$variant(_) => $in_fiat_shamir_heuristic, )+
64 }
65 }
66
67 $(
68 pub fn $try_into_fn(self) -> Result<$payload, ProofStreamError> {
69 match self {
70 Self::$variant(payload) => Ok(payload),
71 _ => Err(UnexpectedItem {
72 expected: ProofItemVariant::$variant,
73 got: self,
74 }),
75 }
76 }
77 )+
78 }
79
80 impl ProofItemVariant {
81 pub fn payload_static_length(self) -> Option<usize> {
82 match self {
83 $( Self::$variant => <$payload>::static_length(), )+
84 }
85 }
86
87 pub const fn include_in_fiat_shamir_heuristic(self) -> bool {
89 match self {
90 $( Self::$variant => $in_fiat_shamir_heuristic, )+
91 }
92 }
93
94 pub const fn payload_type(self) -> &'static str {
96 match self {
97 $( Self::$variant => stringify!($payload), )+
98 }
99 }
100 }
101 };
102}
103
104proof_items!(
105 MerkleRoot(Digest) => true, try_into_merkle_root,
106 Log2PaddedHeight(u32) => true, try_into_log2_padded_height,
107 OutOfDomainMainRow(Box<MainRow<XFieldElement>>) => true, try_into_out_of_domain_main_row,
108 OutOfDomainAuxRow(Box<AuxiliaryRow>) => true, try_into_out_of_domain_aux_row,
109 OutOfDomainQuotientSegments(QuotientSegments) => true, try_into_out_of_domain_quot_segments,
110 FriPolynomial(Polynomial<'static, XFieldElement>) => true, try_into_fri_polynomial,
111
112 AuthenticationStructure(AuthenticationStructure) => false, try_into_authentication_structure,
134
135 MasterMainTableRows(Vec<MainRow<BFieldElement>>) => false, try_into_master_main_table_rows,
142 MasterAuxTableRows(Vec<AuxiliaryRow>) => false, try_into_master_aux_table_rows,
143 QuotientSegmentsElements(Vec<QuotientSegments>) => false, try_into_quot_segments_elements,
144
145 FriCodeword(Vec<XFieldElement>) => false, try_into_fri_codeword,
148
149 FriResponse(FriResponse) => false, try_into_fri_response,
153);
154
155#[cfg(test)]
156#[cfg_attr(coverage_nightly, coverage(off))]
157pub(crate) mod tests {
158 use std::collections::HashSet;
159
160 use assert2::assert;
161 use assert2::let_assert;
162 use proptest::prelude::*;
163 use strum::IntoEnumIterator;
164
165 use crate::proof::Proof;
166 use crate::proof_stream::ProofStream;
167 use crate::shared_tests::LeavedMerkleTreeTestData;
168 use crate::tests::proptest;
169 use crate::tests::test;
170
171 use super::*;
172
173 #[macro_rules_attr::apply(proptest)]
174 fn serialize_fri_response_in_isolation(leaved_merkle_tree: LeavedMerkleTreeTestData) {
175 let fri_response = leaved_merkle_tree.into_fri_response();
176 let encoding = fri_response.encode();
177 let_assert!(Ok(decoding) = FriResponse::decode(&encoding));
178 prop_assert_eq!(fri_response, *decoding);
179 }
180
181 #[macro_rules_attr::apply(proptest)]
182 fn serialize_fri_response_in_proof_stream(leaved_merkle_tree: LeavedMerkleTreeTestData) {
183 let fri_response = leaved_merkle_tree.into_fri_response();
184 let mut proof_stream = ProofStream::new();
185 proof_stream.enqueue(ProofItem::FriResponse(fri_response.clone()));
186 let proof: Proof = proof_stream.into();
187
188 let_assert!(Ok(mut proof_stream) = ProofStream::try_from(&proof));
189 let_assert!(Ok(proof_item) = proof_stream.dequeue());
190 let_assert!(Ok(fri_response_) = proof_item.try_into_fri_response());
191 prop_assert_eq!(fri_response, fri_response_);
192 }
193
194 #[macro_rules_attr::apply(proptest)]
195 fn serialize_authentication_structure_in_isolation(
196 leaved_merkle_tree: LeavedMerkleTreeTestData,
197 ) {
198 let auth_structure = leaved_merkle_tree.auth_structure;
199 let encoding = auth_structure.encode();
200 let_assert!(Ok(decoding) = AuthenticationStructure::decode(&encoding));
201 prop_assert_eq!(auth_structure, *decoding);
202 }
203
204 #[macro_rules_attr::apply(proptest)]
205 fn serialize_authentication_structure_in_proof_stream(
206 leaved_merkle_tree: LeavedMerkleTreeTestData,
207 ) {
208 let auth_structure = leaved_merkle_tree.auth_structure;
209 let mut proof_stream = ProofStream::new();
210 proof_stream.enqueue(ProofItem::AuthenticationStructure(auth_structure.clone()));
211 let proof: Proof = proof_stream.into();
212
213 let_assert!(Ok(mut proof_stream) = ProofStream::try_from(&proof));
214 let_assert!(Ok(proof_item) = proof_stream.dequeue());
215 let_assert!(Ok(auth_structure_) = proof_item.try_into_authentication_structure());
216 prop_assert_eq!(auth_structure, auth_structure_);
217 }
218
219 #[macro_rules_attr::apply(test)]
220 fn interpreting_a_merkle_root_as_anything_else_gives_appropriate_error() {
221 let fake_root = Digest::default();
222 let item = ProofItem::MerkleRoot(fake_root);
223 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_authentication_structure());
224 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_fri_response());
225 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_master_main_table_rows());
226 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_master_aux_table_rows());
227 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_out_of_domain_main_row());
228 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_out_of_domain_aux_row());
229 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_out_of_domain_quot_segments());
230 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_log2_padded_height());
231 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_quot_segments_elements());
232 assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_fri_codeword());
233 assert!(let Err(UnexpectedItem{..}) = item.try_into_fri_polynomial());
234 }
235
236 #[macro_rules_attr::apply(test)]
237 fn proof_item_payload_static_length_is_as_expected() {
238 assert!(let Some(_) = ProofItemVariant::MerkleRoot.payload_static_length());
239 assert!(let Some(_) = ProofItemVariant::Log2PaddedHeight.payload_static_length());
240 assert_eq!(None, ProofItemVariant::FriCodeword.payload_static_length());
241 assert_eq!(None, ProofItemVariant::FriResponse.payload_static_length());
242 }
243
244 #[macro_rules_attr::apply(test)]
245 fn can_loop_over_proof_item_variants() {
246 let all_discriminants: HashSet<_> = ProofItemVariant::iter()
247 .map(|variant| variant.bfield_codec_discriminant())
248 .collect();
249 assert_eq!(ProofItem::COUNT, all_discriminants.len());
250 }
251
252 #[macro_rules_attr::apply(test)]
253 fn proof_item_and_its_variant_have_same_bfield_codec_discriminant() {
254 assert_eq!(
255 ProofItem::MerkleRoot(Digest::default()).bfield_codec_discriminant(),
256 ProofItemVariant::MerkleRoot.bfield_codec_discriminant()
257 );
258 assert_eq!(
259 ProofItem::Log2PaddedHeight(0).bfield_codec_discriminant(),
260 ProofItemVariant::Log2PaddedHeight.bfield_codec_discriminant()
261 );
262 assert_eq!(
263 ProofItem::FriCodeword(vec![]).bfield_codec_discriminant(),
264 ProofItemVariant::FriCodeword.bfield_codec_discriminant()
265 );
266 }
267
268 #[macro_rules_attr::apply(test)]
269 fn proof_item_variants_payload_type_has_expected_format() {
270 assert_eq!("Digest", ProofItemVariant::MerkleRoot.payload_type());
271 }
272}