@@ -57,30 +57,125 @@ impl Secp256k1 {
5757 }
5858}
5959
60- // we use this for both polynomials over scalars and over projective points
60+ fn precalculated_factorials_and_inverses ( n : usize ) -> ( Vec < Fr > , Vec < Fr > , Vec < Fr > ) {
61+ let factorial: Vec < Fr > = std:: iter:: once ( Fr :: one ( ) )
62+ . chain ( ( 1 ..n) . scan ( Fr :: one ( ) , |state, i| {
63+ * state *= Fr :: from ( i as u64 ) ;
64+ Some ( * state)
65+ } ) )
66+ . collect ( ) ;
67+
68+ // inv_fact[i] = 1 / factorial[i]
69+ let inv_factorial: Vec < Fr > = ( 0 ..n)
70+ . rev ( )
71+ . scan (
72+ factorial[ n - 1 ]
73+ . inverse ( )
74+ . expect ( "This is guaranteed to be non-zero" ) ,
75+ |cur_state, i| {
76+ let ith_value = * cur_state;
77+ * cur_state *= Fr :: from ( i as u64 ) ;
78+ Some ( ith_value)
79+ } ,
80+ )
81+ . collect :: < Vec < _ > > ( )
82+ . into_iter ( )
83+ . rev ( )
84+ . collect ( ) ;
85+
86+ let inv: Vec < Fr > = ( 0 ..n)
87+ . map ( |i| {
88+ if i == 0 {
89+ Fr :: zero ( ) //This should never be used
90+ } else {
91+ inv_factorial[ i] * factorial[ i - 1 ]
92+ }
93+ } )
94+ . collect ( ) ;
95+
96+ ( factorial, inv_factorial, inv)
97+ }
98+
99+ // This polynomial is in the (point, value) form instead of coefficient form (points are integers in range [0, degree] converted to Fr's)
100+ // It's used both for polynomials with scalar and projective point domains
61101#[ derive( Clone , Debug , Serialize , Deserialize ) ]
62102pub struct Polynomial < T > ( Vec < T > ) ;
63103
64104impl < T > Polynomial < T >
65105where
66- for < ' a > T : Add < T , Output = T > + Mul < & ' a Fr , Output = T > + Clone ,
106+ for < ' a > T : Add < T , Output = T > + Mul < & ' a Fr , Output = T > + Clone + Zero ,
67107{
68- // todo: max x an int
69- fn eval_at ( & self , x : Fr ) -> T {
70- // Horner's method
71- let mut iter = self . 0 . iter ( ) . rev ( ) ;
72- let mut acc = iter
73- . next ( )
74- . expect ( "polynomial must have at least one coefficient" )
75- . clone ( ) ;
76- for coeff in iter {
77- acc = coeff. clone ( ) + acc * & x;
108+ #[ allow( dead_code) ]
109+ // naive lagrange interpolation, used for testing
110+ fn eval_at ( & self , x : usize ) -> T {
111+ if x < self . 0 . len ( ) {
112+ return self . 0 [ x] . clone ( ) ;
78113 }
79- acc
114+
115+ let x_fr = Fr :: from ( x as u32 ) ;
116+ self . 0
117+ . iter ( )
118+ . enumerate ( )
119+ . fold ( T :: zero ( ) , |result, ( i, y_i) | {
120+ let x_i = Fr :: from ( i as u64 ) ;
121+ // Compute L_i(x)
122+ let ( num, denum) = self . 0 . iter ( ) . enumerate ( ) . filter ( |( j, _) | * j != i) . fold (
123+ ( Fr :: one ( ) , Fr :: one ( ) ) ,
124+ |( num, denum) , ( j, _) | {
125+ let x_j = Fr :: from ( j as u64 ) ;
126+ ( num * ( x_fr - x_j) , denum * ( x_i - x_j) )
127+ } ,
128+ ) ;
129+
130+ // calculate li = num / denum = num * denum^{-1}
131+ let denum_inv = denum. inverse ( ) . expect ( "x_i - x_j must be nonzero" ) ;
132+ let li = num * denum_inv;
133+
134+ result + y_i. clone ( ) * & li
135+ } )
136+ }
137+
138+ /// evaluates the function at smallest consecutive integer points bigger than the degree
139+ /// functions similar to [`lagrange_interpolate_whole_polynomial`],
140+ fn eval_at_suffix_points ( & self , n_points : usize ) -> Vec < T > {
141+ let n_known = self . 0 . len ( ) ;
142+ let n = n_known + n_points;
143+ let ( factorial, inv_factorial, inv) = precalculated_factorials_and_inverses ( n) ;
144+
145+ // For x, calculates the multiplication of (x - i) for all i in known_points (known_points = 0..=degree)
146+ // returns the inverse of the multiplication result, if its one of the known points
147+ let get_coeff = |x : usize | {
148+ if x < n_known {
149+ //inverse
150+ let mut result = inv_factorial[ x] * inv_factorial[ n_known - 1 - x] ;
151+ if ( n_known - x) . is_multiple_of ( 2 ) {
152+ result *= -Fr :: one ( ) ;
153+ }
154+ result
155+ } else {
156+ factorial[ x] * inv_factorial[ x - n_known]
157+ }
158+ } ;
159+
160+ let lagrange_basis_polynomial_coeffs: Vec < Fr > = ( 0 ..n_known) . map ( & get_coeff) . collect ( ) ;
161+
162+ ( n_known..n_known + n_points)
163+ . map ( |x| {
164+ let mut result = T :: zero ( ) ;
165+ let nom_coeff = get_coeff ( x) ;
166+ for i in 0 ..n_known {
167+ let whole_coeff: Fr =
168+ lagrange_basis_polynomial_coeffs[ i] * inv[ x - i] * nom_coeff;
169+ result = result + self . 0 [ i] . clone ( ) * & whole_coeff;
170+ }
171+ result
172+ } )
173+ . collect ( )
80174 }
81175}
82176
83177impl Polynomial < Fr > {
178+ // Generate with points with Fr coordinates in the range [0, degree] for efficient commitment
84179 pub fn rand ( mut rand : impl Rng , degree : usize ) -> Self {
85180 Self ( ( 0 ..degree + 1 ) . map ( |_| Fr :: rand ( & mut rand) ) . collect ( ) )
86181 }
@@ -89,11 +184,17 @@ impl Polynomial<Fr> {
89184 PolynomialCommits ( Polynomial ( secp. generator_batch_mul ( & self . 0 ) ) )
90185 }
91186
92- // shares are return with 0-based index. However, we evaluate share i at
93- // x = i+1, since the value at x=0 represents the secret
94187 pub fn shares ( & self , num_shares : usize ) -> Vec < ( usize , Fr ) > {
188+ let n_known = self . 0 . len ( ) ;
189+ let suffix_points = self . eval_at_suffix_points ( num_shares - n_known) ;
95190 ( 0 ..num_shares)
96- . map ( |i| ( i, self . eval_at ( Fr :: from ( ( i + 1 ) as u64 ) ) ) )
191+ . map ( |i| {
192+ if i < n_known {
193+ ( i, self . 0 [ i] )
194+ } else {
195+ ( i, suffix_points[ i - n_known] )
196+ }
197+ } )
97198 . collect ( )
98199 }
99200
@@ -116,9 +217,15 @@ pub struct ShareCommits(pub Vec<Projective>);
116217
117218impl ShareCommits {
118219 pub fn verify ( & self , polynomial_commits : & PolynomialCommits ) -> Result < ( ) , String > {
220+ let n_known = polynomial_commits. 0 . 0 . len ( ) ;
221+ let n_unknown = self . 0 . len ( ) - n_known;
222+ let unknown_points = polynomial_commits. 0 . eval_at_suffix_points ( n_unknown) ;
119223 for ( i, share_commit) in self . 0 . iter ( ) . enumerate ( ) {
120- let recomputed_share_commit = polynomial_commits. 0 . eval_at ( Fr :: from ( ( i + 1 ) as u64 ) ) ;
121-
224+ let recomputed_share_commit = if i < n_known {
225+ polynomial_commits. 0 . 0 [ i]
226+ } else {
227+ unknown_points[ i - n_known]
228+ } ;
122229 if share_commit != & recomputed_share_commit {
123230 return Err ( "Share commit verification failed" . to_owned ( ) ) ;
124231 }
@@ -161,40 +268,7 @@ pub fn lagrange_interpolate_whole_polynomial(
161268 assert ! ( !known_points. is_empty( ) || !missing_points. is_empty( ) ) ;
162269
163270 let n = known_points. len ( ) + missing_points. len ( ) ;
164- let factorial: Vec < Fr > = std:: iter:: once ( Fr :: one ( ) )
165- . chain ( ( 1 ..n) . scan ( Fr :: one ( ) , |state, i| {
166- * state *= Fr :: from ( i as u64 ) ;
167- Some ( * state)
168- } ) )
169- . collect ( ) ;
170-
171- // inv_fact[i] = 1 / factorial[i]
172- let inv_factorial: Vec < Fr > = ( 0 ..n)
173- . rev ( )
174- . scan (
175- factorial[ n - 1 ]
176- . inverse ( )
177- . expect ( "This is guaranteed to be non-zero" ) ,
178- |cur_state, i| {
179- let ith_value = * cur_state;
180- * cur_state *= Fr :: from ( i as u64 ) ;
181- Some ( ith_value)
182- } ,
183- )
184- . collect :: < Vec < _ > > ( )
185- . into_iter ( )
186- . rev ( )
187- . collect ( ) ;
188-
189- let inv: Vec < Fr > = ( 0 ..n)
190- . map ( |i| {
191- if i == 0 {
192- Fr :: zero ( ) //This should never be used
193- } else {
194- inv_factorial[ i] * factorial[ i - 1 ]
195- }
196- } )
197- . collect ( ) ;
271+ let ( factorial, inv_factorial, inv) = precalculated_factorials_and_inverses ( n) ;
198272
199273 // For x, calculates the multiplication of (x - i) for all i in known_points (known_points = 0..n \ missing_points)
200274 // returns the inverse of the multiplication result, based on the parameter
@@ -252,18 +326,6 @@ mod tests {
252326 use rand:: { SeedableRng , seq:: index:: sample} ;
253327 use rand_chacha:: ChaCha20Rng ;
254328 use std:: collections:: HashSet ;
255- #[ test]
256- fn test_polynomial_eval ( ) {
257- let polynomial = Polynomial :: < Fr > :: rand ( rand:: thread_rng ( ) , 2 ) ;
258-
259- match polynomial. 0 . as_slice ( ) {
260- & [ a, b, c] => {
261- let x = Fr :: rand ( & mut rand:: thread_rng ( ) ) ;
262- assert_eq ! ( polynomial. eval_at( x) , a + b * x + c * x * x) ;
263- }
264- _ => unreachable ! ( ) ,
265- }
266- }
267329
268330 #[ test]
269331 fn test_commit_verification ( ) {
@@ -324,7 +386,7 @@ mod tests {
324386 . map ( |x| x + 1 )
325387 . collect :: < Vec < _ > > ( ) ;
326388 let polynomial = Polynomial :: rand ( seed_rng, n_revealed - 1 ) ;
327- let points = polynomial. shares ( n_total) ; //points[i].0 = i
389+ let points = polynomial. shares ( n_total) ;
328390
329391 let aux_set: HashSet < _ > = hidden_points. iter ( ) . copied ( ) . collect ( ) ;
330392 let known_points: Vec < ( usize , Fr ) > = points
@@ -336,6 +398,23 @@ mod tests {
336398
337399 for ( x, y) in hidden_points. into_iter ( ) . zip ( answer. into_iter ( ) ) {
338400 assert_eq ! ( points[ x] . 1 , y) ;
401+ assert_eq ! ( polynomial. eval_at( x) , y) ;
402+ }
403+ }
404+ }
405+
406+ #[ test]
407+ fn test_suffix_interpolation ( ) {
408+ for ( n_revealed, n_hidden) in vec ! [ ( 5usize , 2usize ) , ( 100 , 10 ) , ( 175 , 7 ) ] {
409+ // Assumes one of the revealed ones is 0, as it will be in application, includes it in the n_revealed ones
410+ let n_total = n_revealed + n_hidden;
411+ let seed_rng = ChaCha20Rng :: seed_from_u64 ( 42 ) ;
412+ let polynomial = Polynomial :: rand ( seed_rng, n_revealed - 1 ) ;
413+ let points = polynomial. shares ( n_total) ;
414+ let answer = polynomial. eval_at_suffix_points ( n_hidden) ;
415+ for ( x, y) in ( n_revealed..n_total) . into_iter ( ) . zip ( answer. into_iter ( ) ) {
416+ assert_eq ! ( points[ x] . 1 , y) ;
417+ assert_eq ! ( polynomial. eval_at( x) , y) ;
339418 }
340419 }
341420 }
0 commit comments