openvm_native_recursion/challenger/
multi_field32.rs1use openvm_native_compiler::ir::{Array, Builder, Config, Ext, Felt, RVar, Var};
2use openvm_stark_backend::p3_field::{
3 max_absorb_injective_limbs, squeeze_field_order_num_limbs, PrimeCharacteristicRing,
4};
5
6use crate::{
7 challenger::{
8 CanCheckWitness, CanObserveDigest, CanObserveVariable, CanSampleBitsVariable,
9 CanSampleVariable, ChallengerVariable, FeltChallenger,
10 },
11 digest::DigestVariable,
12 outer_poseidon2::{Poseidon2CircuitBuilder, RATE, SPONGE_SIZE},
13 utils::{absorb_radix_bits_for_config, reduce_packed, split_pf_to_field_order_limbs},
14 vars::OuterDigestVariable,
15};
16
17#[derive(Clone)]
22pub struct MultiField32ChallengerVariable<C: Config> {
23 sponge_state: [Var<C::N>; SPONGE_SIZE],
24 inner_output_buffer: Vec<Var<C::N>>,
25 f_buffer: Vec<Felt<C::F>>,
26 f_squeeze_buffer: Vec<Felt<C::F>>,
27 absorb_num_f_elms: usize,
28 squeeze_num_f_elms: usize,
29}
30
31impl<C: Config> MultiField32ChallengerVariable<C> {
32 pub fn new(builder: &mut Builder<C>) -> Self {
33 assert!(builder.flags.static_only);
34 MultiField32ChallengerVariable::<C> {
35 sponge_state: [
36 builder.eval(C::N::ZERO),
37 builder.eval(C::N::ZERO),
38 builder.eval(C::N::ZERO),
39 ],
40 inner_output_buffer: vec![],
41 f_buffer: vec![],
42 f_squeeze_buffer: vec![],
43 absorb_num_f_elms: max_absorb_injective_limbs::<C::F, C::N>(),
44 squeeze_num_f_elms: squeeze_field_order_num_limbs::<C::N, C::F>(),
45 }
46 }
47
48 fn absorb_rate_padded_with_tag(
50 &mut self,
51 builder: &mut Builder<C>,
52 values: &[Var<C::N>],
53 length_tag: u8,
54 ) {
55 assert!(values.len() <= RATE);
56 self.inner_output_buffer.clear();
57
58 for (i, &value) in values.iter().enumerate() {
62 self.sponge_state[i] = builder.eval(value);
63 }
64 for i in values.len()..RATE {
65 self.sponge_state[i] = builder.eval(C::N::ZERO);
66 }
67 self.sponge_state[RATE] = builder.eval(self.sponge_state[RATE] + C::N::from_u8(length_tag));
68
69 builder.p2_permute_mut(self.sponge_state);
70 self.inner_output_buffer
71 .extend_from_slice(&self.sponge_state[..RATE]);
72 }
73
74 fn duplexing(&mut self, builder: &mut Builder<C>) {
76 builder.p2_permute_mut(self.sponge_state);
77 self.inner_output_buffer.clear();
78 self.inner_output_buffer
79 .extend_from_slice(&self.sponge_state[..RATE]);
80 }
81
82 fn flush_f_if_non_empty(&mut self, builder: &mut Builder<C>) {
83 if self.f_buffer.is_empty() {
84 return;
85 }
86 let n_in = self.f_buffer.len();
87 assert!(n_in <= self.absorb_num_f_elms * RATE);
88 let radix_bits = absorb_radix_bits_for_config::<C>();
89 let packed = self
90 .f_buffer
91 .chunks(self.absorb_num_f_elms)
92 .map(|f_chunk| reduce_packed(builder, f_chunk, radix_bits))
93 .collect::<Vec<_>>();
94 self.absorb_rate_padded_with_tag(builder, &packed, n_in as u8);
95 self.f_buffer.clear();
96 self.f_squeeze_buffer.clear();
97 }
98
99 fn refill_f_squeeze_from_inner(&mut self, builder: &mut Builder<C>) {
100 self.f_squeeze_buffer.clear();
101 for &pf_val in &self.inner_output_buffer {
102 let f_vals = split_pf_to_field_order_limbs(builder, pf_val, self.squeeze_num_f_elms);
103 self.f_squeeze_buffer.extend(f_vals);
104 }
105 self.inner_output_buffer.clear();
107 }
108
109 pub fn observe(&mut self, builder: &mut Builder<C>, value: Felt<C::F>) {
110 self.inner_output_buffer.clear();
111 self.f_squeeze_buffer.clear();
112 self.f_buffer.push(value);
113 if self.f_buffer.len() == self.absorb_num_f_elms * RATE {
114 self.flush_f_if_non_empty(builder);
115 }
116 }
117
118 pub fn observe_commitment(&mut self, builder: &mut Builder<C>, value: OuterDigestVariable<C>) {
119 self.inner_output_buffer.clear();
120 self.f_squeeze_buffer.clear();
121 self.flush_f_if_non_empty(builder);
122
123 for chunk in value.chunks(RATE) {
124 self.absorb_rate_padded_with_tag(builder, chunk, chunk.len() as u8);
125 self.f_squeeze_buffer.clear();
126 }
127 }
128
129 pub fn sample(&mut self, builder: &mut Builder<C>) -> Felt<C::F> {
130 self.flush_f_if_non_empty(builder);
131 if self.f_squeeze_buffer.is_empty() {
132 if self.inner_output_buffer.is_empty() {
135 self.duplexing(builder);
136 }
137 self.refill_f_squeeze_from_inner(builder);
138 }
139 self.f_squeeze_buffer
140 .pop()
141 .expect("output buffer should be non-empty")
142 }
143
144 pub fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<C::F, C::EF> {
145 let a = self.sample(builder);
146 let b = self.sample(builder);
147 let c = self.sample(builder);
148 let d = self.sample(builder);
149 builder.felts2ext(&[a, b, c, d])
150 }
151
152 pub fn sample_bits(&mut self, builder: &mut Builder<C>, bits: usize) -> Var<C::N> {
153 let rand_f = self.sample(builder);
154 let rand_f_bits = builder.num2bits_f_circuit(rand_f);
155 builder.bits2num_v_circuit(&rand_f_bits[0..bits])
156 }
157
158 pub fn check_witness(&mut self, builder: &mut Builder<C>, bits: usize, witness: Felt<C::F>) {
159 if bits == 0 {
160 return;
161 }
162 self.observe(builder, witness);
163 let element = self.sample_bits(builder, bits);
164 builder.assert_var_eq(element, C::N::from_usize(0));
165 }
166}
167
168impl<C: Config> CanObserveVariable<C, Felt<C::F>> for MultiField32ChallengerVariable<C> {
169 fn observe(&mut self, builder: &mut Builder<C>, value: Felt<C::F>) {
170 MultiField32ChallengerVariable::observe(self, builder, value);
171 }
172
173 fn observe_slice(&mut self, builder: &mut Builder<C>, values: Array<C, Felt<C::F>>) {
174 values.vec().into_iter().for_each(|value| {
175 self.observe(builder, value);
176 });
177 }
178}
179
180impl<C: Config> CanSampleVariable<C, Felt<C::F>> for MultiField32ChallengerVariable<C> {
181 fn sample(&mut self, builder: &mut Builder<C>) -> Felt<C::F> {
182 MultiField32ChallengerVariable::sample(self, builder)
183 }
184}
185
186impl<C: Config> CanSampleBitsVariable<C> for MultiField32ChallengerVariable<C> {
187 fn sample_bits(
188 &mut self,
189 builder: &mut Builder<C>,
190 nb_bits: RVar<C::N>,
191 ) -> Array<C, Var<C::N>> {
192 let rand_f = self.sample(builder);
193 let rand_f_bits = builder.num2bits_f_circuit(rand_f);
194 builder.vec(rand_f_bits[..nb_bits.value()].to_vec())
195 }
196}
197
198impl<C: Config> CanObserveDigest<C> for MultiField32ChallengerVariable<C> {
199 fn observe_digest(&mut self, builder: &mut Builder<C>, commitment: DigestVariable<C>) {
200 if let DigestVariable::Var(v_commit) = commitment {
201 MultiField32ChallengerVariable::observe_commitment(
202 self,
203 builder,
204 v_commit.vec().try_into().unwrap(),
205 );
206 } else {
207 panic!("MultiField32ChallengerVariable expects Var commitment");
208 }
209 }
210}
211
212impl<C: Config> FeltChallenger<C> for MultiField32ChallengerVariable<C> {
213 fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<C::F, C::EF> {
214 MultiField32ChallengerVariable::sample_ext(self, builder)
215 }
216}
217
218impl<C: Config> CanCheckWitness<C> for MultiField32ChallengerVariable<C> {
219 fn check_witness(&mut self, builder: &mut Builder<C>, nb_bits: usize, witness: Felt<C::F>) {
220 MultiField32ChallengerVariable::check_witness(self, builder, nb_bits, witness);
221 }
222}
223
224impl<C: Config> ChallengerVariable<C> for MultiField32ChallengerVariable<C> {
225 fn new(builder: &mut Builder<C>) -> Self {
226 MultiField32ChallengerVariable::new(builder)
227 }
228}
229