1use alloc::vec;
7
8use miden_core::{Felt, field::QuadFelt};
9use miden_crypto::{
10 field::Field,
11 hash::{
12 blake::Blake3Hasher,
13 keccak::{Keccak256Hash, KeccakF, VECTOR_LEN},
14 poseidon2::Poseidon2Permutation256,
15 rpo::RpoPermutation256,
16 rpx::RpxPermutation256,
17 },
18 stark::{
19 GenericStarkConfig,
20 challenger::{CanObserve, DuplexChallenger, HashChallenger, SerializingChallenger64},
21 dft::Radix2DitParallel,
22 fri::PcsParams,
23 hasher::{ChainingHasher, SerializingStatefulSponge, StatefulSponge},
24 lmcs::LmcsConfig,
25 symmetric::{
26 CompressionFunctionFromHasher, CryptographicPermutation, PaddingFreeSponge,
27 TruncatedPermutation,
28 },
29 },
30};
31
32pub type MidenStarkConfig<L, Ch> =
41 GenericStarkConfig<Felt, QuadFelt, L, Radix2DitParallel<Felt>, Ch>;
42
43type PackedFelt = <Felt as Field>::Packing;
44
45const COMPRESSION_INPUTS: usize = 2;
47
48const LOG_BLOWUP: u8 = 3;
53pub const LOG_FOLDING_ARITY: u8 = 2;
55const LOG_FINAL_DEGREE: u8 = 7;
57pub const FOLDING_POW_BITS: usize = 4;
59pub const DEEP_POW_BITS: usize = 12;
61const NUM_QUERIES: usize = 27;
63const QUERY_POW_BITS: usize = 16;
65
66pub fn pcs_params() -> PcsParams {
68 PcsParams::new(
69 LOG_BLOWUP,
70 LOG_FOLDING_ARITY,
71 LOG_FINAL_DEGREE,
72 FOLDING_POW_BITS,
73 DEEP_POW_BITS,
74 NUM_QUERIES,
75 QUERY_POW_BITS,
76 )
77 .expect("invalid PCS parameters")
78}
79
80pub const RELATION_DIGEST: [Felt; 4] = [
88 Felt::new(9663888320842941557),
89 Felt::new(5569923100392661778),
90 Felt::new(10686243500486164404),
91 Felt::new(9017524969302659247),
92];
93
94pub fn observe_protocol_params(challenger: &mut impl CanObserve<Felt>, log_trace_height: u64) {
101 challenger.observe(Felt::new(NUM_QUERIES as u64));
103 challenger.observe(Felt::new(QUERY_POW_BITS as u64));
104 challenger.observe(Felt::new(DEEP_POW_BITS as u64));
105 challenger.observe(Felt::new(FOLDING_POW_BITS as u64));
106 challenger.observe(Felt::new(LOG_BLOWUP as u64));
107 challenger.observe(Felt::new(LOG_FINAL_DEGREE as u64));
108 challenger.observe(Felt::new(1_u64 << LOG_FOLDING_ARITY));
109 challenger.observe(Felt::ZERO);
110
111 challenger.observe(Felt::new(log_trace_height));
113 for _ in 1..SPONGE_RATE {
114 challenger.observe(Felt::ZERO);
115 }
116}
117
118pub fn observe_var_len_public_inputs<C: CanObserve<Felt>>(
125 challenger: &mut C,
126 var_len_public_inputs: &[&[Felt]],
127 message_widths: &[usize],
128) {
129 assert_eq!(
130 var_len_public_inputs.len(),
131 message_widths.len(),
132 "must provide one message width per VLPI group"
133 );
134 for (group, &msg_width) in var_len_public_inputs.iter().zip(message_widths) {
135 assert!(msg_width > 0, "VLPI message width must be positive");
136 let padded_width = msg_width.next_multiple_of(SPONGE_RATE);
137 for message in group.chunks(msg_width) {
138 assert_eq!(
139 message.len(),
140 msg_width,
141 "VLPI group has trailing elements that don't form a complete message"
142 );
143 let mut padded = vec![Felt::ZERO; padded_width];
144 for (i, &elem) in message.iter().enumerate() {
145 padded[padded_width - 1 - i] = elem;
146 }
147 challenger.observe_slice(&padded);
148 }
149 }
150}
151
152const SPONGE_WIDTH: usize = 12;
157const SPONGE_RATE: usize = 8;
159const DIGEST_WIDTH: usize = 4;
161const CAPACITY_RANGE: core::ops::Range<usize> = SPONGE_RATE..SPONGE_WIDTH;
163
164type AlgLmcs<P> = LmcsConfig<
166 PackedFelt,
167 PackedFelt,
168 StatefulSponge<P, SPONGE_WIDTH, SPONGE_RATE, DIGEST_WIDTH>,
169 TruncatedPermutation<P, COMPRESSION_INPUTS, DIGEST_WIDTH, SPONGE_WIDTH>,
170 SPONGE_WIDTH,
171 DIGEST_WIDTH,
172>;
173
174type AlgChallenger<P> = DuplexChallenger<Felt, P, SPONGE_WIDTH, SPONGE_RATE>;
176
177pub type Poseidon2Config =
179 MidenStarkConfig<AlgLmcs<Poseidon2Permutation256>, AlgChallenger<Poseidon2Permutation256>>;
180
181pub fn rpo_config(
183 params: PcsParams,
184) -> MidenStarkConfig<AlgLmcs<RpoPermutation256>, AlgChallenger<RpoPermutation256>> {
185 alg_config(params, RpoPermutation256)
186}
187
188pub fn poseidon2_config(
190 params: PcsParams,
191) -> MidenStarkConfig<AlgLmcs<Poseidon2Permutation256>, AlgChallenger<Poseidon2Permutation256>> {
192 alg_config(params, Poseidon2Permutation256)
193}
194
195pub fn rpx_config(
197 params: PcsParams,
198) -> MidenStarkConfig<AlgLmcs<RpxPermutation256>, AlgChallenger<RpxPermutation256>> {
199 alg_config(params, RpxPermutation256)
200}
201
202fn alg_config<P>(params: PcsParams, perm: P) -> MidenStarkConfig<AlgLmcs<P>, AlgChallenger<P>>
208where
209 P: CryptographicPermutation<[Felt; SPONGE_WIDTH]> + Copy,
210{
211 let lmcs = LmcsConfig::new(StatefulSponge::new(perm), TruncatedPermutation::new(perm));
212 let mut state = [Felt::ZERO; SPONGE_WIDTH];
213 state[CAPACITY_RANGE].copy_from_slice(&RELATION_DIGEST);
214 let challenger = DuplexChallenger {
215 sponge_state: state,
216 input_buffer: vec![],
217 output_buffer: vec![],
218 permutation: perm,
219 };
220 GenericStarkConfig::new(params, lmcs, Radix2DitParallel::default(), challenger)
221}
222
223const BLAKE_DIGEST_SIZE: usize = 32;
228
229type BlakeLmcs = LmcsConfig<
231 Felt,
232 u8,
233 ChainingHasher<Blake3Hasher>,
234 CompressionFunctionFromHasher<Blake3Hasher, COMPRESSION_INPUTS, BLAKE_DIGEST_SIZE>,
235 BLAKE_DIGEST_SIZE,
236 BLAKE_DIGEST_SIZE,
237>;
238
239type BlakeChallenger =
241 SerializingChallenger64<Felt, HashChallenger<u8, Blake3Hasher, BLAKE_DIGEST_SIZE>>;
242
243pub fn blake3_256_config(params: PcsParams) -> MidenStarkConfig<BlakeLmcs, BlakeChallenger> {
245 let lmcs = LmcsConfig::new(
246 ChainingHasher::new(Blake3Hasher),
247 CompressionFunctionFromHasher::new(Blake3Hasher),
248 );
249 let mut challenger = SerializingChallenger64::from_hasher(vec![], Blake3Hasher);
250 challenger.observe_slice(&RELATION_DIGEST);
251 GenericStarkConfig::new(params, lmcs, Radix2DitParallel::default(), challenger)
252}
253
254const KECCAK_WIDTH: usize = 25;
259const KECCAK_RATE: usize = 17;
261const KECCAK_DIGEST: usize = 4;
263const KECCAK_CHALLENGER_DIGEST_SIZE: usize = 32;
265
266type KeccakMmcsSponge = PaddingFreeSponge<KeccakF, KECCAK_WIDTH, KECCAK_RATE, KECCAK_DIGEST>;
268
269type KeccakLmcs = LmcsConfig<
271 [Felt; VECTOR_LEN],
272 [u64; VECTOR_LEN],
273 SerializingStatefulSponge<StatefulSponge<KeccakF, KECCAK_WIDTH, KECCAK_RATE, KECCAK_DIGEST>>,
274 CompressionFunctionFromHasher<KeccakMmcsSponge, COMPRESSION_INPUTS, KECCAK_DIGEST>,
275 KECCAK_WIDTH,
276 KECCAK_DIGEST,
277>;
278
279type KeccakChallenger =
281 SerializingChallenger64<Felt, HashChallenger<u8, Keccak256Hash, KECCAK_CHALLENGER_DIGEST_SIZE>>;
282
283pub fn keccak_config(params: PcsParams) -> MidenStarkConfig<KeccakLmcs, KeccakChallenger> {
288 let mmcs_sponge = KeccakMmcsSponge::new(KeccakF {});
289 let compress = CompressionFunctionFromHasher::new(mmcs_sponge);
290 let sponge = SerializingStatefulSponge::new(StatefulSponge::new(KeccakF {}));
291 let lmcs = LmcsConfig::new(sponge, compress);
292 let mut challenger = SerializingChallenger64::from_hasher(vec![], Keccak256Hash {});
293 challenger.observe_slice(&RELATION_DIGEST);
294 GenericStarkConfig::new(params, lmcs, Radix2DitParallel::default(), challenger)
295}