@@ -204,7 +204,10 @@ pub fn lagrange_interpolation<C: Curve>(
204204 "zero secret share id" ,
205205 ) ) ) ;
206206 }
207- let mut r = C :: Point :: identity ( ) ;
207+ // Pre-allocate vectors for batch processing
208+ let mut terms = Vec :: with_capacity ( k) ;
209+ let mut denominators = Vec :: with_capacity ( k) ;
210+
208211 for i in 0 ..k {
209212 let mut b = x_vec[ i] ;
210213 for j in 0 ..k {
@@ -219,32 +222,110 @@ pub fn lagrange_interpolation<C: Curve>(
219222 b. mul_assign ( & v) ;
220223 }
221224 }
222- let li0 = a. mul ( & b. invert ( ) ) ;
223- let tmp = y_vec[ i] . mul_scalar ( & li0) ;
224- r = r. add ( & tmp) ;
225+ denominators. push ( b) ;
226+ }
227+
228+ let inv_denominators = batch_invert :: < C > ( & denominators) ;
229+
230+ for i in 0 ..k {
231+ let li0 = a. mul ( & inv_denominators[ i] ) ;
232+ terms. push ( y_vec[ i] . mul_scalar ( & li0) ) ;
225233 }
226- Ok ( r)
234+
235+ // Batch add all terms
236+ Ok ( batch_add_points :: < C > ( & terms) )
227237}
228238
229239#[ allow( clippy:: assign_op_pattern) ]
230- pub fn agg_coefficients < C : Curve > (
231- verification_vectors : & [ Vec < C :: Point > ] ,
232- ids : & [ C :: Scalar ] ,
233- ) -> Vec < C :: Point > {
234- let mut final_cfs = Vec :: new ( ) ;
235- for i in 0 ..verification_vectors[ 0 ] . len ( ) {
236- let mut sum = C :: Point :: identity ( ) ;
240+ pub fn agg_coefficients < C : Curve > ( verification_vectors : & [ Vec < C :: Point > ] ) -> Vec < C :: Point > {
241+ if verification_vectors. is_empty ( ) {
242+ return Vec :: new ( ) ;
243+ }
244+ let num_vectors = verification_vectors. len ( ) ;
245+ let vector_len = verification_vectors[ 0 ] . len ( ) ;
246+
247+ // Pre-allocate the result vector
248+ let mut final_cfs = Vec :: with_capacity ( vector_len) ;
249+
250+ // Batch point additions for better cache locality and performance
251+ for i in 0 ..vector_len {
252+ // Collect all points at position i for batch addition
253+ let mut points_to_sum = Vec :: with_capacity ( num_vectors) ;
237254 for v in verification_vectors {
238- sum = sum . add ( & v[ i] ) ;
255+ points_to_sum . push ( v[ i] ) ;
239256 }
257+
258+ // Perform batched addition
259+ let sum = batch_add_points :: < C > ( & points_to_sum) ;
240260 final_cfs. push ( sum) ;
241261 }
242- let mut final_keys = Vec :: new ( ) ;
243- for id in ids. iter ( ) {
244- let tmp = evaluate_polynomial :: < C > ( & final_cfs, id) ;
245- final_keys. push ( tmp) ;
262+ final_cfs
263+ }
264+
265+ // Optimized batch point addition function for elliptic curve operations
266+ pub fn batch_add_points < C : Curve > ( points : & [ C :: Point ] ) -> C :: Point {
267+ if points. is_empty ( ) {
268+ return C :: Point :: identity ( ) ;
269+ }
270+
271+ if points. len ( ) == 1 {
272+ return points[ 0 ] ;
273+ }
274+
275+ // For small numbers of points, use sequential addition
276+ if points. len ( ) <= 4 {
277+ let mut sum = points[ 0 ] ;
278+ for point in & points[ 1 ..] {
279+ sum = sum. add ( point) ;
280+ }
281+ return sum;
282+ }
283+
284+ // For larger numbers, use a binary tree approach to reduce depth
285+ let mut current_points = points. to_vec ( ) ;
286+
287+ while current_points. len ( ) > 1 {
288+ let mut next_level = Vec :: new ( ) ;
289+ let mut i = 0 ;
290+ while i < current_points. len ( ) {
291+ if i + 1 < current_points. len ( ) {
292+ // Add pairs
293+ next_level. push ( current_points[ i] . add ( & current_points[ i + 1 ] ) ) ;
294+ i += 2 ;
295+ } else {
296+ // Handle odd element
297+ next_level. push ( current_points[ i] ) ;
298+ i += 1 ;
299+ }
300+ }
301+ current_points = next_level;
302+ }
303+
304+ current_points[ 0 ]
305+ }
306+
307+ fn batch_invert < C : Curve > ( scalars : & [ C :: Scalar ] ) -> Vec < C :: Scalar > {
308+ if scalars. is_empty ( ) {
309+ return Vec :: new ( ) ;
310+ }
311+
312+ let mut products = Vec :: with_capacity ( scalars. len ( ) ) ;
313+ let mut current_product = C :: Scalar :: from_u32 ( 1 ) ;
314+
315+ for s in scalars {
316+ current_product = current_product. mul ( s) ;
317+ products. push ( current_product) ;
246318 }
247- final_keys
319+
320+ let mut inv = products[ products. len ( ) - 1 ] . invert ( ) ;
321+ let mut result = vec ! [ C :: Scalar :: from_u32( 0 ) ; scalars. len( ) ] ;
322+
323+ for i in ( 1 ..scalars. len ( ) ) . rev ( ) {
324+ result[ i] = inv. mul ( & products[ i - 1 ] ) ;
325+ inv = inv. mul ( & scalars[ i] ) ;
326+ }
327+ result[ 0 ] = inv;
328+ result
248329}
249330
250331#[ cfg( test) ]
@@ -253,40 +334,93 @@ mod tests {
253334 use crate :: types:: * ;
254335 use bls12_381:: * ;
255336
337+ // Helper functions to reduce test code duplication
338+
339+ fn hex_to_bls_g1 ( hex_str : & str ) -> BlsG1 {
340+ let pk_raw: BLSPubkeyRaw = hex:: decode ( hex_str) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
341+ BlsG1 {
342+ g1 : G1Affine :: from_compressed ( & pk_raw) . into_option ( ) . unwrap ( ) ,
343+ }
344+ }
345+
346+ fn hexes_to_bls_g1s ( hex_strings : & [ & str ] ) -> Vec < BlsG1 > {
347+ hex_strings. iter ( ) . map ( |hex| hex_to_bls_g1 ( hex) ) . collect ( )
348+ }
349+
350+ fn hex_to_g1_affine ( hex_str : & str ) -> G1Affine {
351+ let pk_raw: BLSPubkeyRaw = hex:: decode ( hex_str) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
352+ G1Affine :: from_compressed ( & pk_raw) . into_option ( ) . unwrap ( )
353+ }
354+
355+ fn hex_to_g2_affine ( hex_str : & str ) -> G2Affine {
356+ let sig_raw: BLSSignatureRaw = hex:: decode ( hex_str) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
357+ G2Affine :: from_compressed ( & sig_raw) . into_option ( ) . unwrap ( )
358+ }
359+
256360 use super :: * ;
257361
362+ #[ test]
363+ fn test_batch_invert_basic ( ) {
364+ let scalars = vec ! [
365+ BlsScalar :: from_u32( 2 ) ,
366+ BlsScalar :: from_u32( 3 ) ,
367+ BlsScalar :: from_u32( 4 ) ,
368+ BlsScalar :: from_u32( 5 ) ,
369+ ] ;
370+
371+ let inverted = batch_invert :: < BlsG1Curve > ( & scalars) ;
372+
373+ assert_eq ! ( scalars. len( ) , inverted. len( ) ) ;
374+
375+ for i in 0 ..scalars. len ( ) {
376+ let product = scalars[ i] . mul ( & inverted[ i] ) ;
377+ assert_eq ! ( product. scalar, Scalar :: one( ) ) ;
378+ }
379+ }
380+
381+ #[ test]
382+ fn test_batch_invert_empty ( ) {
383+ let scalars: Vec < BlsScalar > = vec ! [ ] ;
384+ let inverted = batch_invert :: < BlsG1Curve > ( & scalars) ;
385+ assert ! ( inverted. is_empty( ) ) ;
386+ }
387+
388+ #[ test]
389+ fn test_batch_invert_single ( ) {
390+ let scalar = BlsScalar :: from_u32 ( 42 ) ;
391+ let scalars = vec ! [ scalar] ;
392+
393+ let inverted = batch_invert :: < BlsG1Curve > ( & scalars) ;
394+
395+ assert_eq ! ( inverted. len( ) , 1 ) ;
396+ assert_eq ! ( inverted[ 0 ] . scalar, scalar. invert( ) . scalar) ;
397+ }
398+
258399 #[ test]
259400 fn test_verify_signature ( ) {
260401 let data = hex:: decode ( "2f901d5cec8722e44afd59e94d0a56bf1506a72a0a60709920aad714d1a2ece0" )
261402 . unwrap ( ) ;
262- let pk: BLSPubkeyRaw = hex:: decode ( "90346f9c5f3c09d96ea02acd0220daa8459f03866ed938c798e3716e42c7e033c9a7ef66a10f83af06d5c00b508c6d0f" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
263- let sig: BLSSignatureRaw = hex:: decode ( "a9c08eff13742f78f1e5929888f223b5b5b12b4836b5417c5a135cf24f4e2a4c66a6cdef91be3098b7e7a6a63903b61302e3cf2b8653101da245cf01a8d82b25debe7b18a3a2eb1778f8628fd2c59c8687f6e048a31250fbc2804c20043b8443" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
264- let pk = G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) ;
265- let sig = G2Affine :: from_compressed ( & sig) . into_option ( ) . unwrap ( ) ;
403+ let pk = hex_to_g1_affine ( "90346f9c5f3c09d96ea02acd0220daa8459f03866ed938c798e3716e42c7e033c9a7ef66a10f83af06d5c00b508c6d0f" ) ;
404+ let sig = hex_to_g2_affine ( "a9c08eff13742f78f1e5929888f223b5b5b12b4836b5417c5a135cf24f4e2a4c66a6cdef91be3098b7e7a6a63903b61302e3cf2b8653101da245cf01a8d82b25debe7b18a3a2eb1778f8628fd2c59c8687f6e048a31250fbc2804c20043b8443" ) ;
266405 assert ! ( bls_verify( & pk, & sig, & data) ) ;
267406
268407 let invalida_data = hex:: decode ( "00" ) . unwrap ( ) ;
269408 assert ! ( !bls_verify( & pk, & sig, & invalida_data) ) ;
270409
271- let wrong_pk: BLSPubkeyRaw = hex:: decode ( "98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
272- let wrong_pk = G1Affine :: from_compressed ( & wrong_pk) . into_option ( ) . unwrap ( ) ;
410+ let wrong_pk = hex_to_g1_affine ( "98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8" ) ;
273411 assert ! ( !bls_verify( & wrong_pk, & sig, & data) ) ;
274412
275- let bad_sig: BLSSignatureRaw = hex:: decode ( "999e7b24bee2587d687e8f358ed10627ef57ec54935bd7a500bbbb18a57e7aa21b800f8b1f487a980d7c93918fdbd8020b66ce9a9e5788a4826e610ac937d8c2ce0ad9c0ee9a5732cf73052493e9a500cc5100a15bdbf9e5b79104db52dbf07c" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
276- let bad_sig = G2Affine :: from_compressed ( & bad_sig) . into_option ( ) . unwrap ( ) ;
413+ let bad_sig = hex_to_g2_affine ( "999e7b24bee2587d687e8f358ed10627ef57ec54935bd7a500bbbb18a57e7aa21b800f8b1f487a980d7c93918fdbd8020b66ce9a9e5788a4826e610ac937d8c2ce0ad9c0ee9a5732cf73052493e9a500cc5100a15bdbf9e5b79104db52dbf07c" ) ;
277414 assert ! ( !bls_verify( & pk, & bad_sig, & data) )
278415 }
279416
280417 #[ test]
281418 fn test_evaluate_polynomial ( ) {
282- let pks: Vec < BlsG1 > = [
419+ let pks = hexes_to_bls_g1s ( & [
283420 "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9" ,
284421 "98876a81fe982573ec5f986956bf9bf0bcb5349d95c3c8da0aefd05a49fea6215f59b0696f906547baed90ab245804e8" ,
285- "ad2c4e5b631fbded449ede4dca2d040b9c7eae58d1e73b3050486c1ba22c15a92d9ff13c05c356f974447e4fca84864a" ]
286- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
287- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
288- } )
289- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
422+ "ad2c4e5b631fbded449ede4dca2d040b9c7eae58d1e73b3050486c1ba22c15a92d9ff13c05c356f974447e4fca84864a" ,
423+ ] ) ;
290424
291425 let target = "af8e0095ecc662f65b95ce57e5bd2f8739ff93b0621a1ad53f5616538d1323ff40e6e9ddd7132298710974fe6fc0344e" ;
292426
@@ -299,14 +433,11 @@ mod tests {
299433
300434 #[ test]
301435 fn test_evaluate_polynomial_bad_base_keys ( ) {
302- let pks: Vec < BlsG1 > = [
436+ let pks = hexes_to_bls_g1s ( & [
303437 "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9" ,
304438 "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9" ,
305- "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9" ]
306- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
307- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
308- } )
309- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
439+ "92cad77a95432bc1030d81b5465cb69be672c1dd0da752230bf8112f8449b03149e7fa208a6fae460a9f0a1d5bd175e9" ,
440+ ] ) ;
310441
311442 let target = "af8e0095ecc662f65b95ce57e5bd2f8739ff93b0621a1ad53f5616538d1323ff40e6e9ddd7132298710974fe6fc0344e" ;
312443
@@ -319,16 +450,13 @@ mod tests {
319450
320451 #[ test]
321452 fn test_lagrange_interpolation ( ) {
322- let pks: Vec < BlsG1 > = [
453+ let pks = hexes_to_bls_g1s ( & [
323454 "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502" ,
324455 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
325456 "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1" ,
326457 "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc" ,
327- "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017" ]
328- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
329- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
330- } )
331- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
458+ "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017" ,
459+ ] ) ;
332460
333461 let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b" ;
334462
@@ -347,17 +475,13 @@ mod tests {
347475
348476 #[ test]
349477 fn test_lagrange_interpolation_out_of_order ( ) {
350- let pks: Vec < BlsG1 > = [
478+ let pks = hexes_to_bls_g1s ( & [
351479 "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017" ,
352480 "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502" ,
353481 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
354482 "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1" ,
355483 "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc" ,
356- ]
357- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
358- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
359- } )
360- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
484+ ] ) ;
361485
362486 let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b" ;
363487
@@ -376,16 +500,13 @@ mod tests {
376500
377501 #[ test]
378502 fn test_lagrange_interpolation_wrong_order ( ) {
379- let pks: Vec < BlsG1 > = [
503+ let pks = hexes_to_bls_g1s ( & [
380504 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
381505 "8da434e68daef9af33e39ab727557a3cd86d7991cd6b545746bf92c8edec37012912cfa2292a21512bce9040a1c0e502" ,
382506 "8cbfb6cb7af927cfe5fb17621df7036de539b7ff4aa0620cdc218d6b7fe7f2e714a96bdeddb2a0dc24867a90594427e1" ,
383507 "9892b390d9d3000c7bf04763006fbc617b7ba9c261fff35094aec3f43599f2c254ae667d9ba135747309b77cd02f1fbc" ,
384- "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017" ]
385- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
386- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
387- } )
388- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
508+ "b255c8a66fd1a13373537e8a4ba258f4990c141fc3c06daccda0711f5ebaffc092f0e5b0e4454e6344e2f97957be4017" ,
509+ ] ) ;
389510
390511 let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b" ;
391512
@@ -404,16 +525,13 @@ mod tests {
404525
405526 #[ test]
406527 fn test_lagrange_interpolation_wrong_base_keys ( ) {
407- let pks: Vec < BlsG1 > = [
528+ let pks = hexes_to_bls_g1s ( & [
408529 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
409530 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
410531 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
411532 "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
412- "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ]
413- . iter ( ) . map ( |pk| -> BLSPubkeyRaw {
414- hex:: decode ( pk) . unwrap ( ) . try_into ( ) . unwrap ( )
415- } )
416- . map ( |pk| BlsG1 { g1 : G1Affine :: from_compressed ( & pk) . into_option ( ) . unwrap ( ) } ) . collect ( ) ;
533+ "a3cd061aab6013f7561978959482d79e9ca636392bc94d4bcad9cb6f90fe2cdf52100f211052f1570db0ca690b6a9903" ,
534+ ] ) ;
417535
418536 let target = "a31d9a483703cd0da9873e5e76b4de5f7035d0a73d79b3be8667daa4fc7065a1bbb5bf77787fcf2a35bd327eecc4fa6b" ;
419537
0 commit comments