LCOV - code coverage report
Current view: top level - ballet/bn254 - fd_bn254.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 169 219 77.2 %
Date: 2026-03-19 18:19:27 Functions: 7 9 77.8 %

          Line data    Source code
       1             : #include "./fd_bn254_internal.h"
       2             : 
       3             : #include "./fd_bn254_field.c"
       4             : #include "./fd_bn254_field_ext.c"
       5             : #include "./fd_bn254_glv.h"
       6             : #include "./fd_bn254_g1.c"
       7             : #include "./fd_bn254_g2.c"
       8             : #include "./fd_bn254_pairing.c"
       9             : 
      10             : /* Compress/Decompress */
      11             : 
      12             : uchar *
      13             : fd_bn254_g1_compress( uchar       out[32],
      14             :                       uchar const in [64],
      15         204 :                       int         big_endian ) {
      16         204 :   fd_bn254_g1_t p[1] = { 0 };
      17         204 :   if( FD_UNLIKELY( !fd_bn254_g1_frombytes_internal( p, in, big_endian ) ) ) {
      18         138 :     return NULL;
      19         138 :   }
      20          66 :   int is_inf   = fd_bn254_g1_is_zero( p );
      21          66 :   int flag_inf = in[ big_endian ? 32 : 63 ] & FLAG_INF;
      22             : 
      23             :   /* Serialize compressed point:
      24             :      https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L122
      25             : 
      26             :      1. If the infinity flags is set, return point at infinity
      27             :      2. Else, copy x and set neg_y flag */
      28             : 
      29          66 :   if( FD_UNLIKELY( is_inf ) ) {
      30          25 :     fd_memset( out, 0, 32 );
      31             :     /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
      32          25 :     out[ big_endian ? 0 : 31 ] |= (uchar)flag_inf;
      33          25 :     return out;
      34          25 :   }
      35             : 
      36          41 :   int is_neg = fd_bn254_fp_is_neg_nm( &p->Y );
      37          41 :   fd_bn254_fp_tobytes_nm( out, &p->X, big_endian );
      38          41 :   if( is_neg ) {
      39          17 :     out[ big_endian ? 0 : 31 ] |= FLAG_NEG;
      40          17 :   }
      41          41 :   return out;
      42          66 : }
      43             : 
      44             : uchar *
      45             : fd_bn254_g1_decompress( uchar       out[64],
      46             :                         uchar const in [32],
      47         198 :                         int         big_endian ) {
      48             :   /* Special case: all zeros in => all zeros out, no flags */
      49         198 :   const uchar zero[32] = { 0 };
      50         198 :   if( fd_memeq( in, zero, 32 ) ) {
      51           8 :     return fd_memset( out, 0, 64UL );
      52           8 :   }
      53             : 
      54         190 :   fd_bn254_fp_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
      55         190 :   int is_inf, is_neg;
      56         190 :   if( FD_UNLIKELY( !fd_bn254_fp_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
      57          58 :     return NULL;
      58          58 :   }
      59             : 
      60             :   /* Point at infinity.
      61             :      If the point at infinity flag is set (bit 6), return the point at
      62             :      infinity with no check on coords.
      63             :      https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160
      64             :   */
      65         132 :   if( is_inf ) {
      66          23 :     fd_memset( out, 0, 64UL );
      67             :     /* no flags */
      68          23 :     return out;
      69          23 :   }
      70             : 
      71         109 :   fd_bn254_fp_to_mont( x, x_nm );
      72         109 :   fd_bn254_fp_sqr( x2, x );
      73         109 :   fd_bn254_fp_mul( x3_plus_b, x2, x );
      74         109 :   fd_bn254_fp_add( x3_plus_b, x3_plus_b, fd_bn254_const_b_mont );
      75         109 :   if( FD_UNLIKELY( !fd_bn254_fp_sqrt( y, x3_plus_b ) ) ) {
      76           2 :     return NULL;
      77           2 :   }
      78             : 
      79         107 :   fd_bn254_fp_from_mont( y, y );
      80         107 :   if( is_neg != fd_bn254_fp_is_neg_nm( y ) ) {
      81          49 :     fd_bn254_fp_neg_nm( y, y );
      82          49 :   }
      83             : 
      84         107 :   fd_bn254_fp_tobytes_nm(  out,     x_nm, big_endian );
      85         107 :   fd_bn254_fp_tobytes_nm( &out[32], y,    big_endian );
      86             :   /* no flags */
      87         107 :   return out;
      88         109 : }
      89             : 
      90             : uchar *
      91             : fd_bn254_g2_compress( uchar       out[64],
      92             :                       uchar const in[128],
      93         262 :                       int         big_endian ) {
      94         262 :   fd_bn254_g2_t p[1] = { 0 };
      95         262 :   if( FD_UNLIKELY( !fd_bn254_g2_frombytes_internal( p, in, big_endian ) ) ) {
      96         224 :     return NULL;
      97         224 :   }
      98          38 :   int is_inf   = fd_bn254_g2_is_zero( p );
      99          38 :   int flag_inf = in[ big_endian ? 64 : 127 ] & FLAG_INF;
     100             : 
     101             :   /* Serialize compressed point */
     102             : 
     103          38 :   if( FD_UNLIKELY( is_inf ) ) {
     104          13 :     fd_memset( out, 0, 64 );
     105             :     /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
     106          13 :     out[ big_endian ? 0 : 63 ] |= (uchar)flag_inf;
     107          13 :     return out;
     108          13 :   }
     109             : 
     110             :   /* Serialize x coordinate. The flags are on the 2nd element.
     111             :      https://github.com/arkworks-rs/algebra/blob/v0.4.2/ff/src/fields/models/quadratic_extension.rs#L700-L702 */
     112          25 :   int is_neg = fd_bn254_fp2_is_neg_nm( &p->Y );
     113          25 :   fd_bn254_fp2_tobytes_nm( out, &p->X, big_endian );
     114          25 :   if( is_neg ) {
     115           9 :     out[ big_endian ? 0 : 63 ] |= FLAG_NEG;
     116           9 :   }
     117          25 :   return out;
     118          38 : }
     119             : 
     120             : uchar *
     121             : fd_bn254_g2_decompress( uchar       out[128],
     122             :                         uchar const in  [64],
     123         266 :                         int         big_endian ) {
     124             :   /* Special case: all zeros in => all zeros out, no flags */
     125         266 :   const uchar zero[64] = { 0 };
     126         266 :   if( fd_memeq( in, zero, 64 ) ) {
     127          31 :     return fd_memset( out, 0, 128UL );
     128          31 :   }
     129             : 
     130         235 :   fd_bn254_fp2_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
     131         235 :   int is_inf, is_neg;
     132         235 :   if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
     133         116 :     return NULL;
     134         116 :   }
     135             : 
     136             :   /* Point at infinity.
     137             :      If the point at infinity flag is set (bit 6), return the point at
     138             :      infinity with no check on coords.
     139             :      https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160 */
     140         119 :   if( is_inf ) {
     141          16 :     fd_memset( out, 0, 128UL );
     142             :     /* no flags */
     143          16 :     return out;
     144          16 :   }
     145             : 
     146         103 :   fd_bn254_fp2_to_mont( x, x_nm );
     147         103 :   fd_bn254_fp2_sqr( x2, x );
     148         103 :   fd_bn254_fp2_mul( x3_plus_b, x2, x );
     149         103 :   fd_bn254_fp2_add( x3_plus_b, x3_plus_b, fd_bn254_const_twist_b_mont );
     150         103 :   if( FD_UNLIKELY( !fd_bn254_fp2_sqrt( y, x3_plus_b ) ) ) {
     151          12 :     return NULL;
     152          12 :   }
     153             : 
     154          91 :   fd_bn254_fp2_from_mont( y, y );
     155          91 :   if( is_neg != fd_bn254_fp2_is_neg_nm( y ) ) {
     156          13 :     fd_bn254_fp2_neg_nm( y, y );
     157          13 :   }
     158             : 
     159          91 :   fd_bn254_fp2_tobytes_nm(  out,     x_nm, big_endian );
     160          91 :   fd_bn254_fp2_tobytes_nm( &out[64], y,    big_endian );
     161             :   /* no flags */
     162          91 :   return out;
     163         103 : }
     164             : 
     165             : /* Ops */
     166             : 
     167             : int
     168             : fd_bn254_g1_add_syscall( uchar       out[64],
     169             :                          uchar const in[],
     170             :                          ulong       in_sz,
     171          21 :                          int         big_endian ) {
     172             :   /* Expected 128-byte input (2 points). Pad input with 0s (only big endian). */
     173          21 :   if( FD_UNLIKELY( in_sz > 128UL ) ) {
     174           1 :     return -1;
     175           1 :   }
     176          20 :   if( FD_UNLIKELY( !big_endian && in_sz != 128UL ) ) {
     177           0 :     return -1;
     178           0 :   }
     179          20 :   uchar FD_ALIGNED buf[128] = { 0 };
     180          20 :   fd_memcpy( buf, in, in_sz );
     181             : 
     182             :   /* Validate inputs */
     183          20 :   fd_bn254_g1_t r[1], a[1], b[1];
     184          20 :   if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
     185           1 :     return -1;
     186           1 :   }
     187          19 :   if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( b, &buf[64], big_endian ) ) ) {
     188           6 :     return -1;
     189           6 :   }
     190             : 
     191             :   /* Compute point add and serialize result */
     192          13 :   fd_bn254_g1_affine_add( r, a, b );
     193          13 :   fd_bn254_g1_tobytes( out, r, big_endian );
     194          13 :   return 0;
     195          19 : }
     196             : 
     197             : int
     198             : fd_bn254_g2_add_syscall( uchar       out[128],
     199             :                          uchar const in[],
     200             :                          ulong       in_sz,
     201           0 :                          int         big_endian ) {
     202             :   /* Expected 256-byte input (2 points).
     203             :      https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L234-L236 */
     204           0 :   if( FD_UNLIKELY( in_sz != 256UL ) ) {
     205           0 :     return -1;
     206           0 :   }
     207           0 :   uchar FD_ALIGNED buf[256] = { 0 };
     208           0 :   fd_memcpy( buf, in, in_sz );
     209             : 
     210             :   /* Validate inputs (curve eq only, no subgroup)
     211             :      https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L238-L250 */
     212           0 :   fd_bn254_g2_t r[1], a[1], b[1];
     213           0 :   if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( a, &buf[ 0], big_endian ) ) ) {
     214           0 :     return -1;
     215           0 :   }
     216           0 :   if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( b, &buf[128], big_endian ) ) ) {
     217           0 :     return -1;
     218           0 :   }
     219             : 
     220             :   /* Compute point add and serialize result */
     221           0 :   fd_bn254_g2_affine_add( r, a, b );
     222           0 :   fd_bn254_g2_tobytes( out, r, big_endian );
     223           0 :   return 0;
     224           0 : }
     225             : 
     226             : int
     227             : fd_bn254_g1_scalar_mul_syscall( uchar       out[64],
     228             :                                 uchar const in[],
     229             :                                 ulong       in_sz,
     230        1777 :                                 int         big_endian ) {
     231             :   /* Expected 96-byte input (1 point + 1 scalar). Pad input with 0s (only big endian). */
     232        1777 :   if( FD_UNLIKELY( in_sz > 96UL ) ) {
     233           2 :     return -1;
     234           2 :   }
     235        1775 :   if( FD_UNLIKELY( !big_endian && in_sz != 96UL ) ) {
     236           0 :     return -1;
     237           0 :   }
     238        1775 :   uchar FD_ALIGNED buf[96] = { 0 };
     239        1775 :   fd_memcpy( buf, in, fd_ulong_min( in_sz, 96UL ) );
     240             : 
     241             :   /* Validate inputs */
     242        1775 :   fd_bn254_g1_t r[1], a[1];
     243        1775 :   fd_bn254_scalar_t s[1];
     244        1775 :   if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
     245        1316 :     return -1;
     246        1316 :   }
     247             : 
     248             :   /* Scalar is big endian and NOT validated
     249             :      https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L211-L214 */
     250         464 :   if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
     251         464 :     fd_uint256_bswap( s, fd_type_pun_const( &buf[64] ) ); /* &buf[64] is always FD_ALIGNED */
     252 >1844*10^16 :   } else {
     253 >1844*10^16 :     memcpy( s, &buf[64], 32 );
     254 >1844*10^16 :   }
     255             :   // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
     256             : 
     257             :   /* Compute scalar mul and serialize result */
     258         459 :   fd_bn254_g1_scalar_mul( r, a, s );
     259         459 :   fd_bn254_g1_tobytes( out, r, big_endian );
     260         459 :   return 0;
     261        1775 : }
     262             : 
     263             : int
     264             : fd_bn254_g2_scalar_mul_syscall( uchar       out[128],
     265             :                                 uchar const in[],
     266             :                                 ulong       in_sz,
     267           0 :                                 int         big_endian ) {
     268             :   /* Expected 160-byte input (1 point + 1 scalar).
     269             :      https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L248-L250 */
     270           0 :   if( FD_UNLIKELY( in_sz != 160UL ) ) {
     271           0 :     return -1;
     272           0 :   }
     273           0 :   uchar FD_ALIGNED buf[160] = { 0 };
     274           0 :   fd_memcpy( buf, in, 160UL );
     275             : 
     276             :   /* Validate point (curve equation and subgroup membership)
     277             :      https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L252-L255 */
     278           0 :   fd_bn254_g2_t r[1], a[1];
     279           0 :   fd_bn254_scalar_t s[1];
     280           0 :   if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
     281           0 :     return -1;
     282           0 :   }
     283             : 
     284             :   /* Scalar is little endian and NOT validated
     285             :      https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L256-L272 */
     286           0 :   if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
     287           0 :     fd_uint256_bswap( s, fd_type_pun_const( &buf[128] ) ); /* &buf[128] is always FD_ALIGNED */
     288           0 :   } else {
     289           0 :     memcpy( s, &buf[128], 32 );
     290           0 :   }
     291             :   // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
     292             : 
     293             :   /* Compute scalar mul and serialize result */
     294           0 :   fd_bn254_g2_scalar_mul( r, a, s );
     295           0 :   fd_bn254_g2_tobytes( out, r, big_endian );
     296           0 :   return 0;
     297           0 : }
     298             : 
     299             : int
     300             : fd_bn254_pairing_is_one_syscall( uchar       out[32],
     301             :                                  uchar const in[],
     302             :                                  ulong       in_sz,
     303             :                                  int         big_endian,
     304         478 :                                  int         check_len ) {
     305             :   /* https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L244
     306             :      Note: Solana had a bug where it checked if input.len().checked_rem(192).is_none(),
     307             :      which only fails when dividing by zero, so the check never triggered.
     308             :      When check_len is true, we properly validate that input size is a multiple of 192.
     309             :      This corresponds to the fix_alt_bn128_pairing_length_check feature gate. */
     310         478 :   if( check_len ) {
     311           0 :     if( FD_UNLIKELY( (in_sz % 192UL) != 0 ) ) {
     312           0 :       return -1; /* Invalid input length */
     313           0 :     }
     314           0 :   }
     315         478 :   ulong elements_len = in_sz / 192UL;
     316         478 :   fd_bn254_g1_t p[FD_BN254_PAIRING_BATCH_MAX];
     317         478 :   fd_bn254_g2_t q[FD_BN254_PAIRING_BATCH_MAX];
     318             : 
     319             :   /* Important: set r=1 so that the result of 0 pairings is 1. */
     320         478 :   fd_bn254_fp12_t r[1];
     321         478 :   fd_bn254_fp12_set_one( r );
     322             : 
     323         478 :   ulong sz=0;
     324         597 :   for( ulong i=0; i<elements_len; i++ ) {
     325             :     /* G1: deserialize and check subgroup membership */
     326         505 :     if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( &p[sz], &in[i*192   ], big_endian ) ) ) {
     327         148 :       return -1;
     328         148 :     }
     329             :     /* G2: deserialize and check subgroup membership */
     330         357 :     if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( &q[sz], &in[i*192+64], big_endian ) ) ) {
     331         238 :       return -1;
     332         238 :     }
     333             :     /* Skip any pair where either P or Q is the point at infinity */
     334         119 :     if( FD_UNLIKELY( fd_bn254_g1_is_zero(&p[sz]) || fd_bn254_g2_is_zero(&q[sz]) ) ) {
     335          34 :       continue;
     336          34 :     }
     337          85 :     ++sz;
     338             :     /* Compute the Miller loop and aggregate into r */
     339          85 :     if( sz==FD_BN254_PAIRING_BATCH_MAX || i==elements_len-1 ) {
     340          52 :       fd_bn254_fp12_t tmp[1];
     341          52 :       fd_bn254_miller_loop( tmp, p, q, sz );
     342          52 :       fd_bn254_fp12_mul( r, r, tmp );
     343          52 :       sz = 0;
     344          52 :     }
     345          85 :   }
     346          92 :   if( sz>0 ) {
     347           0 :     fd_bn254_fp12_t tmp[1];
     348           0 :     fd_bn254_miller_loop( tmp, p, q, sz );
     349           0 :     fd_bn254_fp12_mul( r, r, tmp );
     350           0 :     sz = 0;
     351           0 :   }
     352             : 
     353             :   /* Compute the final exponentiation */
     354          92 :   fd_bn254_final_exp( r, r );
     355             : 
     356             :   /* Output is 0 or 1, serialized as big endian uint256. */
     357          92 :   fd_memset( out, 0, 32 );
     358          92 :   if( FD_LIKELY( fd_bn254_fp12_is_one( r ) ) ) {
     359          52 :     out[ big_endian ? 31 : 0 ] = 1;
     360          52 :   }
     361          92 :   return 0;
     362         478 : }

Generated by: LCOV version 1.14