LCOV - code coverage report
Current view: top level - ballet/bn254 - fd_bn254.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 171 217 78.8 %
Date: 2026-08-13 04:56:22 Functions: 9 9 100.0 %

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

Generated by: LCOV version 1.14