1use std::{
2 array,
3 borrow::{Borrow, BorrowMut},
4 marker::PhantomData,
5};
6
7use p3_air::Air;
8use p3_baby_bear::BabyBear;
9use p3_commit::TwoAdicMultiplicativeCoset;
10use p3_field::{AbstractField, PrimeField32, TwoAdicField};
11use sp1_core_machine::riscv::RiscvAir;
12use sp1_primitives::{consts::WORD_SIZE, types::RecursionProgramType};
13use sp1_recursion_compiler::{
14 config::InnerConfig,
15 ir::{Array, Builder, Config, Felt, Var},
16 prelude::DslVariable,
17};
18use sp1_recursion_core::{
19 air::{RecursionPublicValues, RECURSIVE_PROOF_NUM_PV_ELTS},
20 runtime::{RecursionProgram, DIGEST_SIZE},
21};
22
23use sp1_recursion_compiler::prelude::*;
24use sp1_stark::{
25 air::{MachineAir, POSEIDON_NUM_WORDS, PV_DIGEST_NUM_WORDS},
26 baby_bear_poseidon2::BabyBearPoseidon2,
27 Com, ShardProof, StarkGenericConfig, StarkMachine, StarkVerifyingKey, Word,
28};
29
30use crate::{
31 challenger::{CanObserveVariable, DuplexChallengerVariable},
32 fri::TwoAdicFriPcsVariable,
33 hints::Hintable,
34 stark::{RecursiveVerifierConstraintFolder, StarkVerifier},
35 types::{ShardProofVariable, VerifyingKeyVariable},
36 utils::{const_fri_config, get_challenger_public_values, hash_vkey, var2felt},
37};
38
39use super::utils::{commit_public_values, verify_public_values_hash};
40
41#[derive(Debug, Clone, Copy)]
42pub struct SP1DeferredVerifier<C: Config, SC: StarkGenericConfig, A> {
43 _phantom: PhantomData<(C, SC, A)>,
44}
45
46pub struct SP1DeferredMemoryLayout<'a, SC: StarkGenericConfig, A: MachineAir<SC::Val>>
48where
49 SC::Val: PrimeField32,
50{
51 pub compress_vk: &'a StarkVerifyingKey<SC>,
52 pub machine: &'a StarkMachine<SC, A>,
53 pub proofs: Vec<ShardProof<SC>>,
54
55 pub start_reconstruct_deferred_digest: Vec<SC::Val>,
56
57 pub is_complete: bool,
58
59 pub sp1_vk: &'a StarkVerifyingKey<SC>,
60 pub sp1_machine: &'a StarkMachine<SC, RiscvAir<SC::Val>>,
61 pub committed_value_digest: Vec<Word<SC::Val>>,
62 pub deferred_proofs_digest: Vec<SC::Val>,
63 pub leaf_challenger: SC::Challenger,
64 pub end_pc: SC::Val,
65 pub end_shard: SC::Val,
66 pub end_execution_shard: SC::Val,
67 pub init_addr_bits: [SC::Val; 32],
68 pub finalize_addr_bits: [SC::Val; 32],
69}
70
71#[derive(DslVariable, Clone)]
73pub struct SP1DeferredMemoryLayoutVariable<C: Config> {
74 pub compress_vk: VerifyingKeyVariable<C>,
75
76 pub proofs: Array<C, ShardProofVariable<C>>,
77
78 pub start_reconstruct_deferred_digest: Array<C, Felt<C::F>>,
79
80 pub is_complete: Var<C::N>,
81
82 pub sp1_vk: VerifyingKeyVariable<C>,
83 pub committed_value_digest: Array<C, Array<C, Felt<C::F>>>,
84 pub deferred_proofs_digest: Array<C, Felt<C::F>>,
85 pub leaf_challenger: DuplexChallengerVariable<C>,
86 pub end_pc: Felt<C::F>,
87 pub end_shard: Felt<C::F>,
88 pub end_execution_shard: Felt<C::F>,
89 pub init_addr_bits: Array<C, Felt<C::F>>,
90 pub finalize_addr_bits: Array<C, Felt<C::F>>,
91}
92
93impl<A> SP1DeferredVerifier<InnerConfig, BabyBearPoseidon2, A>
94where
95 A: MachineAir<BabyBear> + for<'a> Air<RecursiveVerifierConstraintFolder<'a, InnerConfig>>,
96{
97 pub fn build(machine: &StarkMachine<BabyBearPoseidon2, A>) -> RecursionProgram<BabyBear> {
99 let mut builder = Builder::<InnerConfig>::new(RecursionProgramType::Deferred);
100 let input: SP1DeferredMemoryLayoutVariable<_> = builder.uninit();
101 SP1DeferredMemoryLayout::<BabyBearPoseidon2, A>::witness(&input, &mut builder);
102
103 let pcs = TwoAdicFriPcsVariable {
104 config: const_fri_config(&mut builder, machine.config().pcs().fri_config()),
105 };
106
107 SP1DeferredVerifier::verify(&mut builder, &pcs, machine, input);
108
109 builder.halt();
110
111 builder.compile_program()
112 }
113}
114
115impl<C: Config, SC, A> SP1DeferredVerifier<C, SC, A>
116where
117 C::F: PrimeField32 + TwoAdicField,
118 SC: StarkGenericConfig<
119 Val = C::F,
120 Challenge = C::EF,
121 Domain = TwoAdicMultiplicativeCoset<C::F>,
122 >,
123 A: MachineAir<C::F> + for<'a> Air<RecursiveVerifierConstraintFolder<'a, C>>,
124 Com<SC>: Into<[SC::Val; DIGEST_SIZE]>,
125{
126 pub fn verify(
136 builder: &mut Builder<C>,
137 pcs: &TwoAdicFriPcsVariable<C>,
138 machine: &StarkMachine<SC, A>,
139 input: SP1DeferredMemoryLayoutVariable<C>,
140 ) {
141 let SP1DeferredMemoryLayoutVariable {
143 compress_vk,
144 proofs,
145 start_reconstruct_deferred_digest,
146 is_complete,
147 sp1_vk,
148 committed_value_digest,
149 deferred_proofs_digest,
150 leaf_challenger,
151 end_pc,
152 end_shard,
153 end_execution_shard,
154 init_addr_bits,
155 finalize_addr_bits,
156 } = input;
157
158 let mut deferred_public_values_stream: Vec<Felt<_>> =
160 (0..RECURSIVE_PROOF_NUM_PV_ELTS).map(|_| builder.eval(C::F::zero())).collect();
161
162 let deferred_public_values: &mut RecursionPublicValues<_> =
163 deferred_public_values_stream.as_mut_slice().borrow_mut();
164
165 let compress_vk_digest = hash_vkey(builder, &compress_vk);
167
168 deferred_public_values.compress_vk_digest =
169 array::from_fn(|i| builder.get(&compress_vk_digest, i));
170
171 deferred_public_values.start_reconstruct_deferred_digest =
173 array::from_fn(|i| builder.get(&start_reconstruct_deferred_digest, i));
174
175 builder.assert_usize_ne(proofs.len(), 0);
177
178 let mut reconstruct_deferred_digest = builder.array(POSEIDON_NUM_WORDS);
180 for (i, first_digest) in
181 deferred_public_values.start_reconstruct_deferred_digest.iter().enumerate()
182 {
183 builder.set(&mut reconstruct_deferred_digest, i, *first_digest);
184 }
185
186 builder.range(0, proofs.len()).for_each(|i, builder| {
188 let proof = builder.get(&proofs, i);
190
191 let mut challenger = DuplexChallengerVariable::new(builder);
195 challenger.observe(builder, compress_vk.commitment.clone());
197 challenger.observe(builder, compress_vk.pc_start);
198 challenger.observe(builder, proof.commitment.main_commit.clone());
200 for j in 0..machine.num_pv_elts() {
201 let element = builder.get(&proof.public_values, j);
202 challenger.observe(builder, element);
203 }
204
205 StarkVerifier::<C, SC>::verify_shard(
207 builder,
208 &compress_vk,
209 pcs,
210 machine,
211 &mut challenger,
212 &proof,
213 true,
214 );
215
216 let current_public_values_elements = (0..RECURSIVE_PROOF_NUM_PV_ELTS)
218 .map(|i| builder.get(&proof.public_values, i))
219 .collect::<Vec<Felt<_>>>();
220
221 let current_public_values: &RecursionPublicValues<Felt<C::F>> =
222 current_public_values_elements.as_slice().borrow();
223
224 verify_public_values_hash(builder, current_public_values);
226
227 builder.assert_felt_eq(current_public_values.is_complete, C::F::one());
229
230 for (digest, current) in deferred_public_values
232 .compress_vk_digest
233 .iter()
234 .zip(current_public_values.compress_vk_digest.iter())
235 {
236 builder.assert_felt_eq(*digest, *current);
237 }
238
239 let mut poseidon_inputs = builder.array(48);
243 for j in 0..DIGEST_SIZE {
244 let current_digest_element = builder.get(&reconstruct_deferred_digest, j);
245 builder.set(&mut poseidon_inputs, j, current_digest_element);
246 }
247
248 for j in 0..DIGEST_SIZE {
249 builder.set(
251 &mut poseidon_inputs,
252 j + DIGEST_SIZE,
253 current_public_values.sp1_vk_digest[j],
254 );
255 }
256 for j in 0..PV_DIGEST_NUM_WORDS {
257 for k in 0..WORD_SIZE {
258 let element = current_public_values.committed_value_digest[j][k];
261 builder.set(&mut poseidon_inputs, j * WORD_SIZE + k + 16, element);
262 }
263 }
264 let new_digest = builder.poseidon2_hash(&poseidon_inputs);
265 for j in 0..DIGEST_SIZE {
266 let new_value = builder.get(&new_digest, j);
267 builder.set(&mut reconstruct_deferred_digest, j, new_value);
268 }
269 });
270
271 deferred_public_values.start_pc = end_pc;
275 deferred_public_values.next_pc = end_pc;
276 deferred_public_values.start_shard = end_shard;
277 deferred_public_values.next_shard = end_shard;
278 deferred_public_values.start_execution_shard = end_execution_shard;
279 deferred_public_values.next_execution_shard = end_execution_shard;
280 let init_addr_bits = core::array::from_fn(|i| builder.get(&init_addr_bits, i));
282 deferred_public_values.previous_init_addr_bits = init_addr_bits;
283 deferred_public_values.last_init_addr_bits = init_addr_bits;
284 let finalize_addr_bits = core::array::from_fn(|i| builder.get(&finalize_addr_bits, i));
285 deferred_public_values.previous_finalize_addr_bits = finalize_addr_bits;
286 deferred_public_values.last_finalize_addr_bits = finalize_addr_bits;
287
288 let sp1_vk_digest = hash_vkey(builder, &sp1_vk);
290 deferred_public_values.sp1_vk_digest = array::from_fn(|i| builder.get(&sp1_vk_digest, i));
291
292 for (i, public_word) in deferred_public_values.committed_value_digest.iter_mut().enumerate()
294 {
295 let hinted_word = builder.get(&committed_value_digest, i);
296 public_word.0 = array::from_fn(|j| builder.get(&hinted_word, j));
297 }
298
299 deferred_public_values.deferred_proofs_digest =
301 core::array::from_fn(|i| builder.get(&deferred_proofs_digest, i));
302
303 let values = get_challenger_public_values(builder, &leaf_challenger);
305 deferred_public_values.leaf_challenger = values;
306 deferred_public_values.start_reconstruct_challenger = values;
307 deferred_public_values.end_reconstruct_challenger = values;
308
309 deferred_public_values.end_reconstruct_deferred_digest =
311 array::from_fn(|i| builder.get(&reconstruct_deferred_digest, i));
312
313 deferred_public_values.is_complete = var2felt(builder, is_complete);
315
316 commit_public_values(builder, deferred_public_values);
317 }
318}