Skip to content

Commit 6c6abf0

Browse files
author
It's me, CI
committed
Arrabbiata: introduce the concept of "program" in the environment
The structure Program would encapsulate all the information reg. an element of the NP relation. In other words, it contains all the public and private values, including the challenges. It will also contain the input and the expected output that will be passed in public values. In addition to that, the accumulations will also be kept. The environment used by the interpreter will work with two different instances of the program structure, one for each curve cycle.
1 parent eb385e8 commit 6c6abf0

File tree

1 file changed

+238
-2
lines changed

1 file changed

+238
-2
lines changed

arrabbiata/src/witness.rs

+238-2
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

0 commit comments

Comments
 (0)