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: 87 127 68.5 %
Date: 2026-03-31 06:22:16 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           9 :                                           grp_ciph_2h_t const *   grouped_ciphertext ) {
       9           9 :   fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("first-pubkey"),  pubkey1 );
      10           9 :   fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("second-pubkey"), pubkey2 );
      11           9 :   fd_zksdk_transcript_append_message( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext"), (uchar *)grouped_ciphertext, sizeof(grp_ciph_2h_t) );
      12           9 : }
      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           9 :                                                              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           9 :   if( FD_UNLIKELY( fd_memeq( pubkey1,                        fd_ristretto255_compressed_zero, 32 )
      22           9 :                 || fd_memeq( grouped_ciphertext->commitment, fd_ristretto255_compressed_zero, 32 ) ) ) {
      23           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      24           0 :   }
      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           9 :   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           9 :   return fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
      31           9 :     proof,
      32           9 :     pubkey1,
      33           9 :     pubkey2,
      34           9 :     grouped_ciphertext->commitment,
      35           9 :     grouped_ciphertext->handles[0].handle,
      36           9 :     grouped_ciphertext->handles[1].handle,
      37           9 :     NULL,
      38           9 :     NULL,
      39           9 :     NULL,
      40           9 :     NULL,
      41           9 :     0,
      42           9 :     transcript
      43           9 :   );
      44           9 : }
      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           9 :   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             :     When pubkey2 is 0, also proof->y2, handle2 and handle2_hi should be 0.
      80             : 
      81             :     Because of batched, the length of the MSM varies between 9 and 12.
      82             :     Points/scalars 7-8 (if batched) and 11 (if batched) are only
      83             :     included when required.
      84             : 
      85             :     We store points and scalars in the following arrays:
      86             : 
      87             :     non-batched (9 points):       batched (12 points):
      88             : 
      89             :          points  scalars               points  scalars
      90             :      0   G       z_x                0   G       z_x
      91             :      1   H       z_r                1   H       z_r
      92             :      2   Y_1     -w                 2   Y_1     -w
      93             :      3   Y_2     -w^2               3   Y_2     -w^2
      94             :      4   pub1    z_r w              4   pub1    z_r w
      95             :      5   C       -c                 5   C       -c
      96             :      6   h1      -c w               6   h1      -c w
      97             :      7   pub2    z_r w^2            7   C_hi    -c t
      98             :      8   h2      -c w^2             8   h1_hi   -c w t
      99             :     ----------------------- MSM     9   pub2    z_r w^2
     100             :          Y_0                       10   h2      -c w^2
     101             :                                    11   h2_hi   -c w^2 t
     102             :                                    ----------------------- MSM
     103             :                                         Y_0
     104             :   */
     105             : 
     106             :   /* Validate all inputs */
     107           9 :   uchar scalars[ 12 * 32 ];
     108           9 :   fd_ristretto255_point_t points[12];
     109           9 :   fd_ristretto255_point_t y0[1];
     110           9 :   fd_ristretto255_point_t res[1];
     111             : 
     112           9 :   if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zr )==NULL ) ) {
     113           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     114           0 :   }
     115           9 :   if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zx )==NULL ) ) {
     116           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     117           0 :   }
     118             : 
     119           9 :   fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_G );
     120           9 :   fd_ristretto255_point_set( &points[1], fd_zksdk_basepoint_H );
     121           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( y0, proof->y0 )==NULL ) ) {
     122           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     123           0 :   }
     124           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], proof->y1 )==NULL ) ) {
     125           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     126           0 :   }
     127           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], proof->y2 )==NULL ) ) {
     128           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     129           0 :   }
     130           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], pubkey1 )==NULL ) ) {
     131           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     132           0 :   }
     133           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[5], comm )==NULL ) ) {
     134           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     135           0 :   }
     136           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[6], handle1 )==NULL ) ) {
     137           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     138           0 :   }
     139             : 
     140           9 :   ulong idx = 7;
     141           9 :   if( batched ) {
     142           0 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], comm_hi )==NULL ) ) {
     143           0 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     144           0 :     }
     145           0 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle1_hi )==NULL ) ) {
     146           0 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     147           0 :     }
     148           0 :   }
     149             : 
     150           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], pubkey2 )==NULL ) ) {
     151           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     152           0 :   }
     153           9 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2 )==NULL ) ) {
     154           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     155           0 :   }
     156             : 
     157           9 :   if( batched ) {
     158           0 :     if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2_hi )==NULL ) ) {
     159           0 :       return FD_ZKSDK_VERIFY_PROOF_ERROR;
     160           0 :     }
     161           0 :   }
     162             : 
     163             :   /* Finalize transcript and extract challenges */
     164             : 
     165             :   /* 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 */
     166           9 :   fd_zksdk_transcript_domsep_grp_ciph_val_proof( transcript, 2 );
     167             : 
     168             :   /* 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 */
     169           9 :   int val = FD_TRANSCRIPT_SUCCESS;
     170           9 :   val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_0"), proof->y0);
     171           9 :   val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_1"), proof->y1);
     172           9 :   if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
     173           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
     174           0 :   }
     175             :   /* Y_2 can be an all zero point if the pubkey2 is all zero */
     176           9 :   fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_2"), proof->y2);
     177             : 
     178           9 :   uchar c[ 32 ];
     179           9 :   uchar w[ 32 ];
     180           9 :   fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
     181             : 
     182           9 :   fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_r"), proof->zr );
     183           9 :   fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_x"), proof->zx );
     184             : 
     185           9 :   fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") );
     186             : 
     187             :   /* 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
     188             :      Note: we use a slightly different MSM but they're equivalent. */
     189             : 
     190             :   /* Compute scalars */
     191           9 :   fd_curve25519_scalar_set( &scalars[ 0*32 ], proof->zx );           //  z_x
     192           9 :   fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->zr );           //  z_r
     193           9 :   fd_curve25519_scalar_neg( &scalars[ 2*32 ], w );                   // -w
     194           9 :   fd_curve25519_scalar_mul( &scalars[ 3*32 ], &scalars[ 2*32 ], w ); // -w^2
     195           9 :   fd_curve25519_scalar_mul( &scalars[ 4*32 ], proof->zr, w );        //  z_r w
     196           9 :   fd_curve25519_scalar_neg( &scalars[ 5*32 ], c );                   // -c
     197           9 :   fd_curve25519_scalar_mul( &scalars[ 6*32 ], &scalars[ 5*32 ], w ); // -c w
     198           9 :   idx = 7;
     199           9 :   if( batched ) {
     200           0 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 5*32 ], challenge_t ); // -c t
     201           0 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], challenge_t ); // -c w t
     202           0 :   }
     203           9 :   fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 4*32 ], w ); // z_r w^2
     204           9 :   fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], w ); // -c w^2
     205           9 :   if( batched ) {
     206           0 :     fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 8*32 ], w ); // -c w^2 t
     207           0 :   }
     208             : 
     209             :   /* Compute the final MSM */
     210           9 :   fd_ristretto255_multi_scalar_mul( res, scalars, points, idx );
     211             : 
     212             :   /* 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 */
     213           9 :   if( FD_LIKELY( fd_ristretto255_point_eq( res, y0 ) ) ) {
     214           3 :     return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
     215           3 :   }
     216           6 :   return FD_ZKSDK_VERIFY_PROOF_ERROR;
     217           9 : }
     218             : 
     219             : /* 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 */
     220             : int
     221           9 : fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity( void const * _context, void const * _proof ) {
     222           9 :   fd_zksdk_transcript_t transcript[1];
     223           9 :   fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext-validity-2-handles-instruction") );
     224             : 
     225           9 :   fd_zksdk_grp_ciph_2h_val_context_t const * context = _context;
     226           9 :   fd_zksdk_grp_ciph_2h_val_proof_t const *   proof   = _proof;
     227           9 :   return fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity(
     228           9 :     proof,
     229           9 :     context->pubkey1,
     230           9 :     context->pubkey2,
     231           9 :     context->grouped_ciphertext,
     232           9 :     transcript
     233           9 :   );
     234           9 : }

Generated by: LCOV version 1.14