1
1
use ark_ec:: CurveConfig ;
2
2
use ark_ff:: PrimeField ;
3
3
use ark_poly:: Evaluations ;
4
- use kimchi:: circuits:: gate:: CurrOrNext ;
4
+ use kimchi:: circuits:: { domains :: EvaluationDomains , gate:: CurrOrNext } ;
5
5
use log:: debug;
6
6
use mina_poseidon:: constants:: SpongeConstants ;
7
7
use num_bigint:: { BigInt , BigUint } ;
8
8
use num_integer:: Integer ;
9
9
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 _} ;
11
11
use rayon:: iter:: { IntoParallelRefIterator , ParallelIterator } ;
12
12
13
13
use crate :: {
@@ -18,6 +18,242 @@ use crate::{
18
18
setup, NUMBER_OF_COLUMNS , NUMBER_OF_VALUES_TO_ABSORB_PUBLIC_IO ,
19
19
} ;
20
20
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
+
21
257
/// An environment is used to contain the state of a long "running program".
22
258
///
23
259
/// The running program is composed of two parts: one user application and one
@@ -214,11 +450,6 @@ pub struct Env<
214
450
// ---------------
215
451
}
216
452
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.
222
453
impl <
223
454
Fp : PrimeField ,
224
455
Fq : PrimeField ,
0 commit comments