LCOV - code coverage report
Current view: top level - ballet/zksdk/instructions - fd_zksdk_zero_ciphertext.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 55 69 79.7 %
Date: 2026-03-19 18:19:27 Functions: 3 3 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/zero_ciphertext.rs#L167 */
       4             : static inline void
       5             : zero_ciphertext_hash_context( fd_zksdk_transcript_t * transcript,
       6             :                               uchar const             pubkey    [ 32 ],
       7          30 :                               uchar const             ciphertext[ 64 ] ) {
       8          30 :   fd_zksdk_transcript_append_pubkey    ( transcript, FD_TRANSCRIPT_LITERAL("pubkey"),     pubkey );
       9          30 :   fd_zksdk_transcript_append_ciphertext( transcript, FD_TRANSCRIPT_LITERAL("ciphertext"), ciphertext );
      10          30 : }
      11             : 
      12             : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L98 */
      13             : static inline int
      14             : fd_zksdk_verify_proof_zero_ciphertext(
      15             :   fd_zksdk_zero_ciphertext_proof_t const * proof,
      16             :   uchar const                              pubkey    [ 32 ],
      17             :   uchar const                              ciphertext[ 64 ],
      18          32 :   fd_zksdk_transcript_t *                  transcript ) {
      19             :   /*
      20             :     We need to verify the 2 following equivalences.
      21             :     Instead of verifying them one by one, it's more efficient to pack
      22             :     them up in a single MSM (and to do so we have to mul by 1, w).
      23             : 
      24             :     (         z P =?= c H + Y_P         ) * 1
      25             :     (         z D =?= c C + Y_D         ) * w
      26             : 
      27             :     We store points and scalars in the following arrays:
      28             : 
      29             :         points  scalars
      30             :     0   H       -c
      31             :     1   P        z
      32             :     2   C       -wc
      33             :     3   D        wz
      34             :     4   Y_D     -w
      35             :     ----------------------- MSM
      36             :         Y_P
      37             :   */
      38             : 
      39             :   /* Validate all inputs. */
      40          32 :   uchar scalars[ 5 * 32 ];
      41          32 :   fd_ristretto255_point_t points[5];
      42          32 :   fd_ristretto255_point_t y[1];
      43          32 :   fd_ristretto255_point_t res[1];
      44             : 
      45             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L104-L109
      46             :      Reject identity pubkey and ciphertext.commitment. */
      47          32 :   if( FD_UNLIKELY( fd_memeq( pubkey,          fd_ristretto255_compressed_zero, 32 )
      48          32 :                 || fd_memeq( &ciphertext[0],  fd_ristretto255_compressed_zero, 32 )
      49          32 :                 || fd_memeq( &ciphertext[32], fd_ristretto255_compressed_zero, 32 ) ) ) {
      50           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      51           0 :   }
      52             : 
      53             :   /* Validate scalar and decompress all points.
      54             :      Note: Agave does this in various places, but any failure simply results in an invalid proof. */
      55          32 :   if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->z )==NULL ) ) {
      56           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      57           0 :   }
      58             : 
      59          32 :   fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_H );
      60          32 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[1], pubkey )==NULL ) ) {
      61           2 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      62           2 :   }
      63          30 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], &ciphertext[0] )==NULL ) ) {
      64           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      65           0 :   }
      66          30 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], &ciphertext[32] )==NULL ) ) {
      67           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      68           0 :   }
      69          30 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], proof->yd )==NULL ) ) {
      70           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      71           0 :   }
      72          30 :   if( FD_UNLIKELY( fd_ristretto255_point_decompress( y, proof->yp )==NULL ) ) {
      73           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      74           0 :   }
      75             : 
      76             :   /* Finalize transcript and extract challenges */
      77             : 
      78             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L111-L112 */
      79          30 :   zero_ciphertext_hash_context( transcript, pubkey, ciphertext );
      80          30 :   fd_zksdk_transcript_domsep_zero_ciphertext_proof( transcript );
      81             : 
      82             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L119-L126 */
      83          30 :   int val = FD_TRANSCRIPT_SUCCESS;
      84          30 :   val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_P"), proof->yp);
      85          30 :   if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
      86           0 :     return FD_ZKSDK_VERIFY_PROOF_ERROR;
      87           0 :   }
      88          30 :   fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_D"), proof->yd);
      89             : 
      90          30 :   uchar c[ 32 ];
      91          30 :   uchar w[ 32 ];
      92          30 :   fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
      93             : 
      94          30 :   fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z"), proof->z );
      95             : 
      96          30 :   fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") );
      97             : 
      98             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L141
      99             :      Note: we use a slightly different MSM but they're equivalent. */
     100             : 
     101             :   /* Compute scalars */
     102          30 :   fd_curve25519_scalar_neg( &scalars[ 0*32 ], c );                   // -c
     103          30 :   fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->z );            //  z
     104          30 :   fd_curve25519_scalar_mul( &scalars[ 2*32 ], &scalars[ 0*32 ], w ); // -wc
     105          30 :   fd_curve25519_scalar_mul( &scalars[ 3*32 ], w, proof->z );         //  wz
     106          30 :   fd_curve25519_scalar_neg( &scalars[ 4*32 ], w );                   // -w
     107             : 
     108             :   /* Compute the final MSM */
     109          30 :   fd_ristretto255_multi_scalar_mul( res, scalars, points, 5 );
     110             : 
     111             :   /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L160-L164 */
     112          30 :   if( FD_LIKELY( fd_ristretto255_point_eq( res, y ) ) ) {
     113          24 :     return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
     114          24 :   }
     115           6 :   return FD_ZKSDK_VERIFY_PROOF_ERROR;
     116          30 : }
     117             : 
     118             : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs#L88 */
     119             : int
     120          32 : fd_zksdk_instr_verify_proof_zero_ciphertext( void const * _context, void const * _proof ) {
     121          32 :   fd_zksdk_transcript_t transcript[1];
     122          32 :   fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("zero-ciphertext-instruction") );
     123             : 
     124          32 :   fd_zksdk_zero_ciphertext_context_t const * context = _context;
     125          32 :   fd_zksdk_zero_ciphertext_proof_t const *   proof   = _proof;
     126          32 :   return fd_zksdk_verify_proof_zero_ciphertext(
     127          32 :     proof,
     128          32 :     context->pubkey,
     129          32 :     context->ciphertext,
     130          32 :     transcript
     131          32 :   );
     132          32 : }

Generated by: LCOV version 1.14