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

Generated by: LCOV version 1.14