1use p3_baby_bear::{BabyBear, DiffusionMatrixBabyBear};
2use p3_commit::{ExtensionMmcs, TwoAdicMultiplicativeCoset};
3use p3_field::{extension::BinomialExtensionField, AbstractField, Field, TwoAdicField};
4use p3_fri::FriConfig;
5use p3_merkle_tree::FieldMerkleTreeMmcs;
6use p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral};
7use p3_symmetric::{PaddingFreeSponge, TruncatedPermutation};
8use sp1_recursion_compiler::{
9 asm::AsmConfig,
10 ir::{Array, Builder, Config, Felt, MemVariable, Var},
11};
12use sp1_recursion_core::{
13 air::ChallengerPublicValues,
14 runtime::{DIGEST_SIZE, PERMUTATION_WIDTH},
15};
16use sp1_stark::{
17 air::MachineAir, baby_bear_poseidon2::BabyBearPoseidon2, Dom, ShardProof, StarkGenericConfig,
18 StarkMachine, StarkVerifyingKey,
19};
20
21use crate::{
22 challenger::DuplexChallengerVariable,
23 fri::{types::FriConfigVariable, TwoAdicMultiplicativeCosetVariable},
24 stark::EMPTY,
25 types::{QuotientDataValues, VerifyingKeyVariable},
26};
27
28type SC = BabyBearPoseidon2;
29type F = <SC as StarkGenericConfig>::Val;
30type EF = <SC as StarkGenericConfig>::Challenge;
31type C = AsmConfig<F, EF>;
32type Val = BabyBear;
33type Challenge = BinomialExtensionField<Val, 4>;
34type Perm = Poseidon2<Val, Poseidon2ExternalMatrixGeneral, DiffusionMatrixBabyBear, 16, 7>;
35type Hash = PaddingFreeSponge<Perm, 16, 8, 8>;
36type Compress = TruncatedPermutation<Perm, 2, 8, 16>;
37type ValMmcs =
38 FieldMerkleTreeMmcs<<Val as Field>::Packing, <Val as Field>::Packing, Hash, Compress, 8>;
39type ChallengeMmcs = ExtensionMmcs<Val, Challenge, ValMmcs>;
40type RecursionConfig = AsmConfig<Val, Challenge>;
41type RecursionBuilder = Builder<RecursionConfig>;
42
43pub fn const_fri_config(
44 builder: &mut RecursionBuilder,
45 config: &FriConfig<ChallengeMmcs>,
46) -> FriConfigVariable<RecursionConfig> {
47 let two_addicity = Val::TWO_ADICITY;
48 let mut generators = builder.dyn_array(two_addicity);
49 let mut subgroups = builder.dyn_array(two_addicity);
50 for i in 0..two_addicity {
51 let constant_generator = Val::two_adic_generator(i);
52 builder.set(&mut generators, i, constant_generator);
53
54 let constant_domain = TwoAdicMultiplicativeCoset { log_n: i, shift: Val::one() };
55 let domain_value: TwoAdicMultiplicativeCosetVariable<_> = builder.constant(constant_domain);
56 builder.set(&mut subgroups, i, domain_value);
57 }
58 FriConfigVariable {
59 log_blowup: builder.eval(BabyBear::from_canonical_usize(config.log_blowup)),
60 blowup: builder.eval(BabyBear::from_canonical_usize(1 << config.log_blowup)),
61 num_queries: builder.eval(BabyBear::from_canonical_usize(config.num_queries)),
62 proof_of_work_bits: builder.eval(BabyBear::from_canonical_usize(config.proof_of_work_bits)),
63 subgroups,
64 generators,
65 }
66}
67
68pub fn clone<T: MemVariable<C>>(builder: &mut RecursionBuilder, var: &T) -> T {
69 let mut arr = builder.dyn_array(1);
70 builder.set(&mut arr, 0, var.clone());
71 builder.get(&arr, 0)
72}
73
74pub fn clone_array<T: MemVariable<C>>(
75 builder: &mut RecursionBuilder,
76 arr: &Array<C, T>,
77) -> Array<C, T> {
78 let mut new_arr = builder.dyn_array(arr.len());
79 builder.range(0, arr.len()).for_each(|i, builder| {
80 let var = builder.get(arr, i);
81 builder.set(&mut new_arr, i, var);
82 });
83 new_arr
84}
85
86pub fn felt2var<C: Config>(builder: &mut Builder<C>, felt: Felt<C::F>) -> Var<C::N> {
88 let bits = builder.num2bits_f(felt);
89 builder.bits2num_v(&bits)
90}
91
92pub fn var2felt<C: Config>(builder: &mut Builder<C>, var: Var<C::N>) -> Felt<C::F> {
93 let bits = builder.num2bits_v(var);
94 builder.bits2num_f(&bits)
95}
96
97pub fn assert_challenger_eq_pv<C: Config>(
99 builder: &mut Builder<C>,
100 var: &DuplexChallengerVariable<C>,
101 values: ChallengerPublicValues<Felt<C::F>>,
102) {
103 for i in 0..PERMUTATION_WIDTH {
104 let element = builder.get(&var.sponge_state, i);
105 builder.assert_felt_eq(element, values.sponge_state[i]);
106 }
107 let num_inputs_var = felt2var(builder, values.num_inputs);
108 builder.assert_var_eq(var.nb_inputs, num_inputs_var);
109 let mut input_buffer_array: Array<_, Felt<_>> = builder.dyn_array(PERMUTATION_WIDTH);
110 for i in 0..PERMUTATION_WIDTH {
111 builder.set(&mut input_buffer_array, i, values.input_buffer[i]);
112 }
113 builder.range(0, num_inputs_var).for_each(|i, builder| {
114 let element = builder.get(&var.input_buffer, i);
115 let values_element = builder.get(&input_buffer_array, i);
116 builder.assert_felt_eq(element, values_element);
117 });
118 let num_outputs_var = felt2var(builder, values.num_outputs);
119 builder.assert_var_eq(var.nb_outputs, num_outputs_var);
120 let mut output_buffer_array: Array<_, Felt<_>> = builder.dyn_array(PERMUTATION_WIDTH);
121 for i in 0..PERMUTATION_WIDTH {
122 builder.set(&mut output_buffer_array, i, values.output_buffer[i]);
123 }
124 builder.range(0, num_outputs_var).for_each(|i, builder| {
125 let element = builder.get(&var.output_buffer, i);
126 let values_element = builder.get(&output_buffer_array, i);
127 builder.assert_felt_eq(element, values_element);
128 });
129}
130
131pub fn assign_challenger_from_pv<C: Config>(
133 builder: &mut Builder<C>,
134 dst: &mut DuplexChallengerVariable<C>,
135 values: ChallengerPublicValues<Felt<C::F>>,
136) {
137 for i in 0..PERMUTATION_WIDTH {
138 builder.set(&mut dst.sponge_state, i, values.sponge_state[i]);
139 }
140 let num_inputs_var = felt2var(builder, values.num_inputs);
141 builder.assign(dst.nb_inputs, num_inputs_var);
142 for i in 0..PERMUTATION_WIDTH {
143 builder.set(&mut dst.input_buffer, i, values.input_buffer[i]);
144 }
145 let num_outputs_var = felt2var(builder, values.num_outputs);
146 builder.assign(dst.nb_outputs, num_outputs_var);
147 for i in 0..PERMUTATION_WIDTH {
148 builder.set(&mut dst.output_buffer, i, values.output_buffer[i]);
149 }
150}
151
152pub fn get_challenger_public_values<C: Config>(
153 builder: &mut Builder<C>,
154 var: &DuplexChallengerVariable<C>,
155) -> ChallengerPublicValues<Felt<C::F>> {
156 let sponge_state = core::array::from_fn(|i| builder.get(&var.sponge_state, i));
157 let num_inputs = var2felt(builder, var.nb_inputs);
158 let input_buffer = core::array::from_fn(|i| builder.get(&var.input_buffer, i));
159 let num_outputs = var2felt(builder, var.nb_outputs);
160 let output_buffer = core::array::from_fn(|i| builder.get(&var.output_buffer, i));
161
162 ChallengerPublicValues { sponge_state, num_inputs, input_buffer, num_outputs, output_buffer }
163}
164
165pub fn hash_vkey<C: Config>(
168 builder: &mut Builder<C>,
169 vk: &VerifyingKeyVariable<C>,
170) -> Array<C, Felt<C::F>> {
171 let domain_slots: Var<_> = builder.eval(vk.prep_domains.len() * 4);
172 let vkey_slots: Var<_> = builder.constant(C::N::from_canonical_usize(DIGEST_SIZE + 1));
173 let total_slots: Var<_> = builder.eval(vkey_slots + domain_slots);
174 let mut inputs = builder.dyn_array(total_slots);
175 builder.range(0, DIGEST_SIZE).for_each(|i, builder| {
176 let element = builder.get(&vk.commitment, i);
177 builder.set(&mut inputs, i, element);
178 });
179 builder.set(&mut inputs, DIGEST_SIZE, vk.pc_start);
180 let four: Var<_> = builder.constant(C::N::from_canonical_usize(4));
181 let one: Var<_> = builder.constant(C::N::one());
182 builder.range(0, vk.prep_domains.len()).for_each(|i, builder| {
183 let sorted_index = builder.get(&vk.preprocessed_sorted_idxs, i);
184 let domain = builder.get(&vk.prep_domains, i);
185 let log_n_index: Var<_> = builder.eval(vkey_slots + sorted_index * four);
186 let size_index: Var<_> = builder.eval(log_n_index + one);
187 let shift_index: Var<_> = builder.eval(size_index + one);
188 let g_index: Var<_> = builder.eval(shift_index + one);
189 let log_n_felt = var2felt(builder, domain.log_n);
190 let size_felt = var2felt(builder, domain.size);
191 builder.set(&mut inputs, log_n_index, log_n_felt);
192 builder.set(&mut inputs, size_index, size_felt);
193 builder.set(&mut inputs, shift_index, domain.shift);
194 builder.set(&mut inputs, g_index, domain.g);
195 });
196 builder.poseidon2_hash(&inputs)
197}
198
199pub(crate) fn get_sorted_indices<SC: StarkGenericConfig, A: MachineAir<SC::Val>>(
200 machine: &StarkMachine<SC, A>,
201 proof: &ShardProof<SC>,
202) -> Vec<usize> {
203 machine
204 .chips_sorted_indices(proof)
205 .into_iter()
206 .map(|x| match x {
207 Some(x) => x,
208 None => EMPTY,
209 })
210 .collect()
211}
212
213pub(crate) fn get_preprocessed_data<SC: StarkGenericConfig, A: MachineAir<SC::Val>>(
214 machine: &StarkMachine<SC, A>,
215 vk: &StarkVerifyingKey<SC>,
216) -> (Vec<usize>, Vec<Dom<SC>>) {
217 let chips = machine.chips();
218 let (prep_sorted_indices, prep_domains) = machine
219 .preprocessed_chip_ids()
220 .into_iter()
221 .map(|chip_idx| {
222 let name = chips[chip_idx].name().clone();
223 let prep_sorted_idx = vk.chip_ordering[&name];
224 (prep_sorted_idx, vk.chip_information[prep_sorted_idx].1)
225 })
226 .unzip();
227 (prep_sorted_indices, prep_domains)
228}
229
230pub(crate) fn get_chip_quotient_data<SC: StarkGenericConfig, A: MachineAir<SC::Val>>(
231 machine: &StarkMachine<SC, A>,
232 proof: &ShardProof<SC>,
233) -> Vec<QuotientDataValues> {
234 machine
235 .shard_chips_ordered(&proof.chip_ordering)
236 .map(|chip| {
237 let log_quotient_degree = chip.log_quotient_degree();
238 QuotientDataValues { log_quotient_degree, quotient_size: 1 << log_quotient_degree }
239 })
240 .collect()
241}