LCOV - code coverage report
Current view: top level - ballet/zksdk/instructions - fd_zksdk_grouped_ciphertext_2_handles_validity.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 127 127 100.0 %
Date: 2026-11-11 12:40:43 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "../fd_zksdk_private.h"
       2             : 
       3             : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L161 */
       4             : static inline void
       5             : grouped_ciphertext_validity_hash_context( fd_zksdk_transcript_t * transcript,
       6             :                                           uchar const             pubkey1 [ 32 ],
       7             :                                           uchar const             pubkey2 [ 32 ],
       8         208 :                                           grp_ciph_2h_t const *   grouped_ciphertext ) {
       9         208 :   fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("first-pubkey"),  pubkey1 );
      10         208 :   fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("second-pubkey"), pubkey2 );
      11         208 :   fd_zksdk_transcript_append_message( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext"), (uchar *)grouped_ciphertext, sizeof(grp_ciph_2h_t) );
      12         208 : }
      13             : 
      14             : static inline int
      15             : fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity( fd_zksdk_grp_ciph_2h_val_proof_t const * proof,
      16             :                                                              uchar const                              pubkey1 [ 32 ],
      17             :                                                              uchar const                              pubkey2 [ 32 ],
      18             :                                                              grp_ciph_2h_t const *                    grouped_ciphertext,
      19         211 :                                                              fd_zksdk_transcript_t *                  transcript ) {
      20             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L155-L159 */
      21         211 :   if( FD_UNLIKELY( fd_memeq( pubkey1,                        fd_ristretto255_compressed_zero, 32 )
      22         211 :                 || fd_memeq( grouped_ciphertext->commitment, fd_ristretto255_compressed_zero, 32 ) ) ) {
      23           3 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      24           3 :   }
      25             : 
      26             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L161-L166 */
      27         208 :   grouped_ciphertext_validity_hash_context( transcript, pubkey1, pubkey2, grouped_ciphertext );
      28             : 
      29             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L167 */
      30         208 :   return fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
      31         208 :     proof,
      32         208 :     pubkey1,
      33         208 :     pubkey2,
      34         208 :     grouped_ciphertext->commitment,
      35         208 :     grouped_ciphertext->handles[0].handle,
      36         208 :     grouped_ciphertext->handles[1].handle,
      37         208 :     NULL,
      38         208 :     NULL,
      39         208 :     NULL,
      40         208 :     NULL,
      41         208 :     0,
      42         208 :     transcript
      43         208 :   );
      44         211 : }
      45             : 
      46             : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L170
      47             :    In Agave, the verify_direct() is a method of the NON-batched proof.
      48             :    The batched proof is converted into a non-batched proof with 3 mul.
      49             :    However, verify_direct() is doing a MSM so we can embed the 3 mul
      50             :    as part of it.
      51             :    So, in Firedancer verify_direct optionally supports a batched
      52             :    proof and computes a single, adjusted MSM. */
      53             : int
      54             : fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
      55             :   fd_zksdk_grp_ciph_2h_val_proof_t const * proof,
      56             :   uchar const                              pubkey1    [ 32 ],
      57             :   uchar const                              pubkey2    [ 32 ],
      58             :   uchar const                              comm       [ 32 ],
      59             :   uchar const                              handle1    [ 32 ],
      60             :   uchar const                              handle2    [ 32 ],
      61             :   uchar const                              comm_hi    [ 32 ],
      62             :   uchar const                              handle1_hi [ 32 ],
      63             :   uchar const                              handle2_hi [ 32 ],
      64             :   uchar const                              challenge_t[ 32 ],
      65             :   int   const                              batched,
      66         417 :   fd_zksdk_transcript_t *                  transcript ) {
      67             :   /*
      68             :     We need to verify the 3 following equivalences.
      69             :     Instead of verifying them one by one, it's more efficient to pack
      70             :     them up in a single MSM (and to do so we have to mul by 1, w, w^2).
      71             : 
      72             :     ( z_r H + z_x G =?= c C + Y_0 ) * 1
      73             :     (      z_r pub1 =?= c h1 + Y_1 ) * w
      74             :     (      z_r pub2 =?= c h2 + Y_2 ) * w^2
      75             : 
      76             :     When batched==false, C, h1, h2 are given and C_hi, h1_hi, h2_hi are NULL.
      77             :     When batched==true, they are computed as C = C_lo + t C_hi.
      78             : 
      79             :     An honest proof with identity pubkey2 has identity Y_2 and handles.
      80             :     Verification must not assume this for untrusted proofs: keep those
      81             :     terms in the MSM even when pubkey2 is identity.
      82             : 
      83             :     Because of batched, the length of the MSM varies between 9 and 12.
      84             :     Points/scalars 7-8 (if batched) and 11 (if batched) are only
      85             :     included when required.
      86             : 
      87             :     We store points and scalars in the following arrays:
      88             : 
      89             :     non-batched (9 points):       batched (12 points):
      90             : 
      91             :          points  scalars               points  scalars
      92             :      0   G       z_x                0   G       z_x
      93             :      1   H       z_r                1   H       z_r
      94             :      2   Y_1     -w                 2   Y_1     -w
      95             :      3   Y_2     -w^2               3   Y_2     -w^2
      96             :      4   pub1    z_r w              4   pub1    z_r w
      97             :      5   C       -c                 5   C       -c
      98             :      6   h1      -c w               6   h1      -c w
      99             :      7   pub2    z_r w^2            7   C_hi    -c t
     100             :      8   h2      -c w^2             8   h1_hi   -c w t
     101             :     ----------------------- MSM     9   pub2    z_r w^2
     102             :          Y_0                       10   h2      -c w^2
     103             :                                    11   h2_hi   -c w^2 t
     104             :                                    ----------------------- MSM
     105             :                                         Y_0
     106             :   */
     107             : 
     108             :   /* Validate all inputs */
     109         417 :   uchar scalars[ 12 * 32 ];
     110         417 :   fd_ristretto255_point_t points[12];
     111         417 :   fd_ristretto255_point_t y0[1];
     112         417 :   fd_ristretto255_point_t res[1];
     113             : 
     114         417 :   if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zr )==NULL ) ) {
     115           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     116           2 :   }
     117         415 :   if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zx )==NULL ) ) {
     118           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     119           2 :   }
     120             : 
     121         413 :   fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_G );
     122         413 :   fd_ristretto255_point_set( &points[1], fd_zksdk_basepoint_H );
     123         413 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( y0, proof->y0 )==NULL ) ) {
     124           3 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     125           3 :   }
     126         410 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], proof->y1 )==NULL ) ) {
     127           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     128           2 :   }
     129         408 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], proof->y2 )==NULL ) ) {
     130           1 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     131           1 :   }
     132         407 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], pubkey1 )==NULL ) ) {
     133           4 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     134           4 :   }
     135         403 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[5], comm )==NULL ) ) {
     136           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     137           2 :   }
     138         401 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[6], handle1 )==NULL ) ) {
     139           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     140           2 :   }
     141             : 
     142         399 :   ulong idx = 7;
     143         399 :   if( batched ) {
     144         203 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], comm_hi )==NULL ) ) {
     145           1 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     146           1 :     }
     147         202 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle1_hi )==NULL ) ) {
     148           1 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     149           1 :     }
     150         202 :   }
     151             : 
     152         397 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], pubkey2 )==NULL ) ) {
     153           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     154           2 :   }
     155         395 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2 )==NULL ) ) {
     156           1 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     157           1 :   }
     158             : 
     159         394 :   if( batched ) {
     160         199 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2_hi )==NULL ) ) {
     161           1 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     162           1 :     }
     163         199 :   }
     164             : 
     165             :   /* Finalize transcript and extract challenges */
     166             : 
     167             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L177 */
     168         393 :   fd_zksdk_transcript_domsep_grp_ciph_val_proof( transcript, 2 );
     169             : 
     170             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L179-L189 */
     171         393 :   int val = FD_TRANSCRIPT_SUCCESS;
     172         393 :   val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_0"), proof->y0);
     173         393 :   val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_1"), proof->y1);
     174         393 :   if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
     175           3 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     176           3 :   }
     177             :   /* Y_2 can be an all zero point if the pubkey2 is all zero */
     178         390 :   fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_2"), proof->y2);
     179             : 
     180         390 :   uchar c[ 32 ];
     181         390 :   uchar w[ 32 ];
     182         390 :   fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
     183             : 
     184         390 :   fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_r"), proof->zr );
     185         390 :   fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_x"), proof->zx );
     186             : 
     187         390 :   fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") );
     188             : 
     189             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L190-L244
     190             :      Note: we use a slightly different MSM but they're equivalent. */
     191             : 
     192             :   /* Compute scalars */
     193         390 :   fd_curve25519_scalar_set( &scalars[ 0*32 ], proof->zx );           //  z_x
     194         390 :   fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->zr );           //  z_r
     195         390 :   fd_curve25519_scalar_neg( &scalars[ 2*32 ], w );                   // -w
     196         390 :   fd_curve25519_scalar_mul( &scalars[ 3*32 ], &scalars[ 2*32 ], w ); // -w^2
     197         390 :   fd_curve25519_scalar_mul( &scalars[ 4*32 ], proof->zr, w );        //  z_r w
     198         390 :   fd_curve25519_scalar_neg( &scalars[ 5*32 ], c );                   // -c
     199         390 :   fd_curve25519_scalar_mul( &scalars[ 6*32 ], &scalars[ 5*32 ], w ); // -c w
     200         390 :   idx = 7;
     201         390 :   if( batched ) {
     202         197 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 5*32 ], challenge_t ); // -c t
     203         197 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], challenge_t ); // -c w t
     204         197 :   }
     205         390 :   fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 4*32 ], w ); // z_r w^2
     206         390 :   fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], w ); // -c w^2
     207         390 :   if( batched ) {
     208         197 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 8*32 ], w ); // -c w^2 t
     209         197 :   }
     210             : 
     211             :   /* Compute the final MSM */
     212         390 :   fd_ristretto255_multi_scalar_mul( res, scalars, points, idx );
     213             : 
     214             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L246-L250 */
     215         390 :   if( FD_LIKELY( fd_ristretto255_point_eq( res, y0 ) ) ) {
     216         378 :     return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
     217         378 :   }
     218          12 :   return FD_ZKSDK_VERIFY_PROOF_ERROR;
     219         390 : }
     220             : 
     221             : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs#L109 */
     222             : int
     223         211 : fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity( void const * _context, void const * _proof ) {
     224         211 :   fd_zksdk_transcript_t transcript[1];
     225         211 :   fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext-validity-2-handles-instruction") );
     226             : 
     227         211 :   fd_zksdk_grp_ciph_2h_val_context_t const * context = _context;
     228         211 :   fd_zksdk_grp_ciph_2h_val_proof_t const *   proof   = _proof;
     229         211 :   return fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity(
     230         211 :     proof,
     231         211 :     context->pubkey1,
     232         211 :     context->pubkey2,
     233         211 :     context->grouped_ciphertext,
     234         211 :     transcript
     235         211 :   );
     236         211 : }

Generated by: LCOV version 1.14