1
1
use crate :: {
2
2
commitment:: Commitment ,
3
+ diff:: Diff ,
3
4
utils:: { decode_into, encode_for_domain} ,
4
5
} ;
5
6
use ark_ff:: PrimeField ;
6
- use ark_poly:: { univariate:: DensePolynomial , EvaluationDomain , Evaluations } ;
7
+ use ark_poly:: {
8
+ univariate:: DensePolynomial , EvaluationDomain , Evaluations , Radix2EvaluationDomain ,
9
+ } ;
7
10
use ark_serialize:: { CanonicalDeserialize , CanonicalSerialize } ;
8
11
use kimchi:: curve:: KimchiCurve ;
9
12
use mina_poseidon:: FqSponge ;
@@ -24,7 +27,7 @@ pub struct FieldBlob<G: CommitmentCurve> {
24
27
pub domain_size : usize ,
25
28
pub commitment : Commitment < G > ,
26
29
#[ serde_as( as = "Vec<o1_utils::serialization::SerdeAs>" ) ]
27
- pub data : Vec < DensePolynomial < G :: ScalarField > > ,
30
+ pub chunks : Vec < DensePolynomial < G :: ScalarField > > ,
28
31
}
29
32
30
33
#[ instrument( skip_all, level = "debug" ) ]
@@ -51,29 +54,29 @@ impl<G: KimchiCurve> FieldBlob<G> {
51
54
let field_elements = encode_for_domain ( & domain, bytes) ;
52
55
let domain_size = domain. size ( ) ;
53
56
54
- let data : Vec < DensePolynomial < G :: ScalarField > > = debug_span ! ( "fft" ) . in_scope ( || {
57
+ let chunks : Vec < DensePolynomial < G :: ScalarField > > = debug_span ! ( "fft" ) . in_scope ( || {
55
58
field_elements
56
59
. par_iter ( )
57
60
. map ( |chunk| Evaluations :: from_vec_and_domain ( chunk. to_vec ( ) , domain) . interpolate ( ) )
58
61
. collect ( )
59
62
} ) ;
60
63
let commitment = {
61
- let chunks = commit_to_blob_data ( srs, & data ) ;
64
+ let chunks = commit_to_blob_data ( srs, & chunks ) ;
62
65
let mut sponge = EFqSponge :: new ( G :: other_curve_sponge_params ( ) ) ;
63
66
Commitment :: from_chunks ( chunks, & mut sponge)
64
67
} ;
65
68
66
69
debug ! (
67
70
"Encoded {:.2} MB into {} polynomials" ,
68
71
bytes. len( ) as f32 / 1_000_000.0 ,
69
- data . len( )
72
+ chunks . len( )
70
73
) ;
71
74
72
75
FieldBlob {
73
76
n_bytes : bytes. len ( ) ,
74
77
domain_size,
75
78
commitment,
76
- data ,
79
+ chunks ,
77
80
}
78
81
}
79
82
@@ -92,7 +95,7 @@ impl<G: KimchiCurve> FieldBlob<G> {
92
95
let mut bytes = Vec :: with_capacity ( blob. n_bytes ) ;
93
96
let mut buffer = vec ! [ 0u8 ; m] ;
94
97
95
- for p in blob. data {
98
+ for p in blob. chunks {
96
99
let evals = p. evaluate_over_domain ( domain) . evals ;
97
100
for x in evals {
98
101
decode_into ( & mut buffer, x) ;
@@ -103,14 +106,44 @@ impl<G: KimchiCurve> FieldBlob<G> {
103
106
bytes. truncate ( blob. n_bytes ) ;
104
107
bytes
105
108
}
109
+
110
+ pub fn update < EFqSponge : FqSponge < G :: BaseField , G , G :: ScalarField > > (
111
+ & mut self ,
112
+ srs : & SRS < G > ,
113
+ domain : & Radix2EvaluationDomain < G :: ScalarField > ,
114
+ diff : Diff < G :: ScalarField > ,
115
+ ) {
116
+ let diff_evaluations = diff. as_evaluations ( domain) ;
117
+ let commitment = {
118
+ let commitment_diffs = diff_evaluations
119
+ . par_iter ( )
120
+ . map ( |evals| srs. commit_evaluations_non_hiding ( * domain, evals) )
121
+ . collect :: < Vec < _ > > ( ) ;
122
+ let mut sponge = EFqSponge :: new ( G :: other_curve_sponge_params ( ) ) ;
123
+ self . commitment . update ( commitment_diffs, & mut sponge)
124
+ } ;
125
+ let chunks: Vec < DensePolynomial < G :: ScalarField > > = diff_evaluations
126
+ . into_par_iter ( )
127
+ . zip ( self . chunks . par_iter ( ) )
128
+ . map ( |( evals, p) | {
129
+ let d_p: DensePolynomial < G :: ScalarField > = evals. interpolate ( ) ;
130
+ p + & d_p
131
+ } )
132
+ . collect ( ) ;
133
+ self . commitment = commitment;
134
+ self . chunks = chunks;
135
+ self . n_bytes = diff. new_byte_len ;
136
+ }
106
137
}
107
138
108
139
#[ cfg( test) ]
109
140
mod tests {
110
141
use crate :: { commitment:: commit_to_field_elems, env} ;
111
142
112
143
use super :: * ;
113
- use crate :: utils:: test_utils:: * ;
144
+ use crate :: { diff:: tests:: * , utils:: test_utils:: * } ;
145
+ use ark_ec:: AffineRepr ;
146
+ use ark_ff:: Zero ;
114
147
use ark_poly:: Radix2EvaluationDomain ;
115
148
use mina_curves:: pasta:: { Fp , Vesta , VestaParameters } ;
116
149
use mina_poseidon:: { constants:: PlonkSpongeConstantsKimchi , sponge:: DefaultFqSponge } ;
@@ -149,11 +182,64 @@ mod tests {
149
182
proptest ! {
150
183
#![ proptest_config( ProptestConfig :: with_cases( 10 ) ) ]
151
184
#[ test]
152
- fn test_user_and_storage_provider_commitments_equal( UserData ( xs) in UserData :: arbitrary( ) )
153
- { let elems = encode_for_domain( & * DOMAIN , & xs) ;
154
- let user_commitments = commit_to_field_elems:: <_, VestaFqSponge >( & * SRS , * DOMAIN , elems) ;
155
- let blob = FieldBlob :: <Vesta >:: encode:: <_, VestaFqSponge >( & * SRS , * DOMAIN , & xs) ;
156
- prop_assert_eq!( user_commitments, blob. commitment) ;
157
- }
185
+ fn test_user_and_storage_provider_commitments_equal( UserData ( xs) in UserData :: arbitrary( ) )
186
+ { let elems = encode_for_domain( & * DOMAIN , & xs) ;
187
+ let user_commitments = commit_to_field_elems:: <_, VestaFqSponge >( & * SRS , * DOMAIN , elems) ;
188
+ let blob = FieldBlob :: <Vesta >:: encode:: <_, VestaFqSponge >( & * SRS , * DOMAIN , & xs) ;
189
+ prop_assert_eq!( user_commitments, blob. commitment) ;
190
+ }
191
+ }
192
+
193
+ fn encode_to_chunk_size ( xs : & [ u8 ] , chunk_size : usize ) -> FieldBlob < Vesta > {
194
+ let mut blob = FieldBlob :: < Vesta > :: encode :: < _ , VestaFqSponge > ( & * SRS , * DOMAIN , xs) ;
195
+ assert ! ( blob. chunks. len( ) <= chunk_size) ;
196
+ {
197
+ let pad = DensePolynomial :: zero ( ) ;
198
+ blob. chunks . resize ( chunk_size, pad) ;
199
+ }
200
+ {
201
+ let pad = PolyComm :: new ( vec ! [ Vesta :: zero( ) ] ) ;
202
+ let mut commitments = blob. commitment . chunks . clone ( ) ;
203
+ commitments. resize ( chunk_size, pad) ;
204
+ let mut sponge = VestaFqSponge :: new ( Vesta :: other_curve_sponge_params ( ) ) ;
205
+ blob. commitment = Commitment :: from_chunks ( commitments, & mut sponge) ;
158
206
}
207
+ blob
208
+ }
209
+
210
+ proptest ! {
211
+ #![ proptest_config( ProptestConfig :: with_cases( 20 ) ) ]
212
+ #[ test]
213
+
214
+ fn test_allow_legal_updates( ( UserData ( xs) , UserData ( ys) ) in
215
+ ( UserData :: arbitrary( ) . prop_flat_map( random_diff) )
216
+ ) {
217
+ // start with some random user data
218
+ let mut xs_blob = FieldBlob :: <Vesta >:: encode:: <_, VestaFqSponge >( & * SRS , * DOMAIN , & xs) ;
219
+ let diff = Diff :: <Fp >:: create( & * DOMAIN , & xs, & ys) . unwrap( ) ;
220
+ xs_blob. update:: <VestaFqSponge >( & * SRS , & * DOMAIN , diff. clone( ) ) ;
221
+
222
+ // check that the user and SP agree on the new data
223
+ let user_commitment = {
224
+ let elems = encode_for_domain( & * DOMAIN , & xs) ;
225
+ let commitment = commit_to_field_elems:: <Vesta , VestaFqSponge >( & * SRS , * DOMAIN , elems) ;
226
+
227
+ let commitment_diffs = diff. as_evaluations( & * DOMAIN )
228
+ . par_iter( )
229
+ . map( |evals| SRS . commit_evaluations_non_hiding( * DOMAIN , evals) )
230
+ . collect:: <Vec <_>>( ) ;
231
+
232
+ let mut sponge = VestaFqSponge :: new( Vesta :: other_curve_sponge_params( ) ) ;
233
+ commitment. update( commitment_diffs, & mut sponge)
234
+
235
+ } ;
236
+
237
+ let ys_blob = encode_to_chunk_size( & ys, xs_blob. chunks. len( ) ) ;
238
+ prop_assert_eq!( user_commitment. clone( ) , ys_blob. commitment. clone( ) ) ;
239
+
240
+ // the updated blob should be the same as if we just start with the new data
241
+ prop_assert_eq!( xs_blob, ys_blob)
242
+ }
243
+
244
+ }
159
245
}
0 commit comments