LCOV - code coverage report
Current view: top level - ballet/bn254 - fd_bn254.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 141 182 77.5 %
Date: 2026-01-18 05:18:35 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14