Skip to content

Commit df3e2af

Browse files
authored
Merge pull request #3075 from o1-labs/arrabbiata/use-program-structure-in-env
Arrabbiata: introduce the structure Program
2 parents fd949ec + 117d96d commit df3e2af

File tree

1 file changed

+238
-7
lines changed

1 file changed

+238
-7
lines changed

arrabbiata/src/witness.rs

+238-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use ark_ec::CurveConfig;
22
use ark_ff::PrimeField;
33
use ark_poly::Evaluations;
4-
use kimchi::circuits::gate::CurrOrNext;
4+
use kimchi::circuits::{domains::EvaluationDomains, gate::CurrOrNext};
55
use log::debug;
66
use mina_poseidon::constants::SpongeConstants;
77
use num_bigint::{BigInt, BigUint};
88
use num_integer::Integer;
99
use o1_utils::field_helpers::FieldHelpers;
10-
use poly_commitment::{commitment::CommitmentCurve, PolyComm, SRS as _};
10+
use poly_commitment::{commitment::CommitmentCurve, ipa::SRS, PolyComm, SRS as _};
1111
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
1212

1313
use crate::{
@@ -18,6 +18,242 @@ use crate::{
1818
setup, NUMBER_OF_COLUMNS, NUMBER_OF_VALUES_TO_ABSORB_PUBLIC_IO,
1919
};
2020

21+
/// A running program that the (folding) interpreter has access to.
22+
pub struct Program<
23+
Fp: PrimeField,
24+
Fq: PrimeField,
25+
E1: ArrabbiataCurve<ScalarField = Fp, BaseField = Fq>,
26+
> where
27+
E1::BaseField: PrimeField,
28+
<<E1 as CommitmentCurve>::Params as CurveConfig>::BaseField: PrimeField,
29+
{
30+
/// Commitments to the accumulated program state.
31+
///
32+
/// In Nova language, this is the commitment to the witness accumulator.
33+
pub accumulated_committed_state: Vec<PolyComm<E1>>,
34+
35+
/// Commitments to the previous program states.
36+
///
37+
/// In Nova language, this is the commitment to the previous witness.
38+
pub previous_committed_state: Vec<PolyComm<E1>>,
39+
40+
/// Accumulated witness for the program state.
41+
///
42+
/// In Nova language, this is the accumulated witness W.
43+
///
44+
/// The size of the outer vector must be equal to the number of columns in
45+
/// the circuit.
46+
/// The size of the inner vector must be equal to the number of rows in
47+
/// the circuit.
48+
pub accumulated_program_state: Vec<Vec<E1::ScalarField>>,
49+
50+
/// List of the accumulated challenges over time.
51+
pub accumulated_challenges: Challenges<BigInt>,
52+
53+
/// Challenges during the last computation.
54+
/// This field is useful to keep track of the challenges that must be
55+
/// verified in circuit.
56+
pub previous_challenges: Challenges<BigInt>,
57+
}
58+
59+
impl<Fp: PrimeField, Fq: PrimeField, E1: ArrabbiataCurve<ScalarField = Fp, BaseField = Fq>>
60+
Program<Fp, Fq, E1>
61+
where
62+
E1::BaseField: PrimeField,
63+
<<E1 as CommitmentCurve>::Params as CurveConfig>::BaseField: PrimeField,
64+
{
65+
pub fn new(srs_size: usize, blinder: E1) -> Self {
66+
// Default set to the blinders. Using double to make the EC scaling happy.
67+
let previous_committed_state: Vec<PolyComm<E1>> = (0..NUMBER_OF_COLUMNS)
68+
.map(|_| PolyComm::new(vec![(blinder + blinder).into()]))
69+
.collect();
70+
71+
// FIXME: zero will not work.
72+
let accumulated_committed_state: Vec<PolyComm<E1>> = (0..NUMBER_OF_COLUMNS)
73+
.map(|_| PolyComm::new(vec![blinder]))
74+
.collect();
75+
76+
let mut accumulated_program_state: Vec<Vec<E1::ScalarField>> =
77+
Vec::with_capacity(NUMBER_OF_COLUMNS);
78+
{
79+
let mut vec: Vec<E1::ScalarField> = Vec::with_capacity(srs_size);
80+
(0..srs_size).for_each(|_| vec.push(E1::ScalarField::zero()));
81+
(0..NUMBER_OF_COLUMNS).for_each(|_| accumulated_program_state.push(vec.clone()));
82+
};
83+
84+
let accumulated_challenges: Challenges<BigInt> = Challenges::default();
85+
86+
let previous_challenges: Challenges<BigInt> = Challenges::default();
87+
88+
Self {
89+
accumulated_committed_state,
90+
previous_committed_state,
91+
accumulated_program_state,
92+
accumulated_challenges,
93+
previous_challenges,
94+
}
95+
}
96+
97+
/// Commit to the program state and updating the environment with the
98+
/// result.
99+
///
100+
/// This method is supposed to be called after a new iteration of the
101+
/// program has been executed.
102+
pub fn commit_state(
103+
&mut self,
104+
srs: &SRS<E1>,
105+
domain: EvaluationDomains<E1::ScalarField>,
106+
witness: Vec<Vec<BigInt>>,
107+
) {
108+
let comms: Vec<PolyComm<E1>> = witness
109+
.par_iter()
110+
.map(|evals| {
111+
let evals: Vec<E1::ScalarField> = evals
112+
.par_iter()
113+
.map(|x| E1::ScalarField::from_biguint(&x.to_biguint().unwrap()).unwrap())
114+
.collect();
115+
let evals = Evaluations::from_vec_and_domain(evals.to_vec(), domain.d1);
116+
srs.commit_evaluations_non_hiding(domain.d1, &evals)
117+
})
118+
.collect();
119+
self.previous_committed_state = comms
120+
}
121+
122+
/// Absorb the last committed program state in the correct sponge.
123+
///
124+
/// For a description of the messages to be given to the sponge, including
125+
/// the expected instantiation, refer to the section "Message Passing" in
126+
/// [crate::interpreter].
127+
pub fn absorb_state(&mut self, digest: BigInt) -> Vec<BigInt> {
128+
let mut sponge = E1::create_new_sponge();
129+
let previous_state: E1::BaseField =
130+
E1::BaseField::from_biguint(&digest.to_biguint().unwrap()).unwrap();
131+
E1::absorb_fq(&mut sponge, previous_state);
132+
self.previous_committed_state
133+
.iter()
134+
.for_each(|comm| E1::absorb_curve_points(&mut sponge, &comm.chunks));
135+
let state: Vec<BigInt> = sponge
136+
.sponge
137+
.state
138+
.iter()
139+
.map(|x| x.to_biguint().into())
140+
.collect();
141+
state
142+
}
143+
144+
/// Simulate an interaction with the verifier by requesting to coin a
145+
/// challenge from the current prover sponge state.
146+
///
147+
/// This method supposes that all the messages have been sent to the
148+
/// verifier previously, and the attribute [self.prover_sponge_state] has
149+
/// been updated accordingly by absorbing all the messages correctly.
150+
///
151+
/// The side-effect of this method will be to run a permutation on the
152+
/// sponge state _after_ coining the challenge.
153+
/// There is an hypothesis on the sponge state that the inner permutation
154+
/// has been correctly executed if the absorbtion rate had been reached at
155+
/// the last absorbtion.
156+
///
157+
/// The challenge will be added to the [self.challenges] attribute at the
158+
/// position given by the challenge `chal`.
159+
///
160+
/// Internally, the method is implemented by simply loading the prover
161+
/// sponge state, and squeezing a challenge from it, relying on the
162+
/// implementation of the sponge. Usually, the challenge would be the first
163+
/// N bits of the first element, but it is left as an implementation detail
164+
/// of the sponge given by the curve.
165+
pub fn coin_challenge(&self, sponge_state: Vec<BigInt>) -> (BigInt, Vec<BigInt>) {
166+
let mut sponge = E1::create_new_sponge();
167+
sponge_state.iter().for_each(|x| {
168+
E1::absorb_fq(
169+
&mut sponge,
170+
E1::BaseField::from_biguint(&x.to_biguint().unwrap()).unwrap(),
171+
)
172+
});
173+
let verifier_answer = E1::squeeze_challenge(&mut sponge).to_biguint().into();
174+
sponge.sponge.poseidon_block_cipher();
175+
let state: Vec<BigInt> = sponge
176+
.sponge
177+
.state
178+
.iter()
179+
.map(|x| x.to_biguint().into())
180+
.collect();
181+
(verifier_answer, state)
182+
}
183+
184+
/// Accumulate the program state (or in other words,
185+
/// the witness), by adding the last computed program state into the
186+
/// program state accumulator.
187+
///
188+
/// This method is supposed to be called after the program state has been
189+
/// committed (by calling [self.commit_state]) and absorbed (by calling
190+
/// [self.absorb_state]). The "relation randomiser" must also have been
191+
/// coined and saved in the environment before, by calling
192+
/// [self.coin_challenge].
193+
///
194+
/// The program state is accumulated into a different accumulator, depending
195+
/// on the curve currently being used.
196+
///
197+
/// This is part of the work the prover of the accumulation/folding scheme.
198+
///
199+
/// This must translate the following equation:
200+
/// ```text
201+
/// acc_(p, n + 1) = acc_(p, n) * chal w
202+
/// OR
203+
/// acc_(q, n + 1) = acc_(q, n) * chal w
204+
/// ```
205+
/// where acc and w are vectors of the same size.
206+
pub fn accumulate_program_state(&mut self, chal: BigInt, witness: Vec<Vec<BigInt>>) {
207+
let modulus: BigInt = E1::ScalarField::modulus_biguint().into();
208+
self.accumulated_program_state = self
209+
.accumulated_program_state
210+
.iter()
211+
.zip(witness.iter()) // This iterate over the columns
212+
.map(|(evals_accumulator, evals_witness)| {
213+
evals_accumulator
214+
.iter()
215+
.zip(evals_witness.iter()) // This iterate over the rows
216+
.map(|(acc, w)| {
217+
let rhs: BigInt = (chal.clone() * w).mod_floor(&modulus);
218+
let rhs: BigUint = rhs.to_biguint().unwrap();
219+
let res = E1::ScalarField::from_biguint(&rhs).unwrap();
220+
*acc + res
221+
})
222+
.collect()
223+
})
224+
.collect();
225+
}
226+
227+
/// Accumulate the committed state by adding the last committed state into
228+
/// the committed state accumulator.
229+
///
230+
/// The commitments are accumulated into a different accumulator, depending
231+
/// on the curve currently being used.
232+
///
233+
/// This is part of the work the prover of the accumulation/folding scheme.
234+
///
235+
/// This must translate the following equation:
236+
/// ```text
237+
/// C_(p, n + 1) = C_(p, n) + chal * comm
238+
/// OR
239+
/// C_(q, n + 1) = C_(q, n) + chal * comm
240+
/// ```
241+
///
242+
/// Note that the committed program state is encoded in
243+
/// [crate::NUMBER_OF_COLUMNS] values, therefore we must iterate over all
244+
/// the columns to accumulate the committed state.
245+
pub fn accumulate_committed_state(&mut self, chal: BigInt) {
246+
let chal: BigUint = chal.to_biguint().unwrap();
247+
let chal: E1::ScalarField = E1::ScalarField::from_biguint(&chal).unwrap();
248+
self.accumulated_committed_state = self
249+
.accumulated_committed_state
250+
.iter()
251+
.zip(self.previous_committed_state.iter())
252+
.map(|(l, r)| l + &r.scale(chal))
253+
.collect();
254+
}
255+
}
256+
21257
/// An environment is used to contain the state of a long "running program".
22258
///
23259
/// The running program is composed of two parts: one user application and one
@@ -214,11 +450,6 @@ pub struct Env<
214450
// ---------------
215451
}
216452

217-
// The condition on the parameters for E1 and E2 is to get the coefficients and
218-
// convert them into biguint.
219-
// The condition SWModelParameters is to get the parameters of the curve as
220-
// biguint to use them to compute the slope in the elliptic curve addition
221-
// algorithm.
222453
impl<
223454
Fp: PrimeField,
224455
Fq: PrimeField,

0 commit comments

Comments
 (0)