LCOV - code coverage report
Current view: top level - flamenco/runtime/program/zksdk - fd_zksdk.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 35 164 21.3 %
Date: 2025-10-13 04:42:14 Functions: 1 2 50.0 %

          Line data    Source code
       1             : #include "fd_zksdk_private.h"
       2             : #include "../../fd_borrowed_account.h"
       3             : #include "../../fd_system_ids.h"
       4             : 
       5             : /* fd_zksdk_process_close_context_state is equivalent to process_close_proof_context()
       6             :    https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L127 */
       7             : int
       8           0 : fd_zksdk_process_close_context_state( fd_exec_instr_ctx_t * ctx ) {
       9           0 : #define ACC_IDX_PROOF (0UL)
      10           0 : #define ACC_IDX_DEST  (1UL)
      11           0 : #define ACC_IDX_OWNER (2UL)
      12             : 
      13           0 :   fd_pubkey_t owner_pubkey[1];
      14           0 :   fd_pubkey_t proof_pubkey[1];
      15           0 :   fd_pubkey_t dest_pubkey[1];
      16             : 
      17             :   /* Obtain the owner pubkey by borrowing the owner account in local scope
      18             :   https://github.com/anza-xyz/agave/blob/master/programs/zk-elgamal-proof/src/lib.rs#L133-L141 */
      19           0 :   do {
      20           0 :     fd_guarded_borrowed_account_t owner_acc = {0};
      21           0 :     int instr_err_code = 0;
      22           0 :     if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, ACC_IDX_OWNER, &instr_err_code ) ) ) {
      23           0 :       if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
      24           0 :       return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
      25           0 :     }
      26             : 
      27           0 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_OWNER, &owner_acc );
      28           0 :     *owner_pubkey = *owner_acc.acct->pubkey;
      29             :     /* implicit drop of borrowed owner_acc */
      30           0 :   } while (0);
      31             : 
      32             :   /* Allocate space for borrowed accounts */
      33           0 :   fd_guarded_borrowed_account_t proof_acc = {0};
      34           0 :   fd_guarded_borrowed_account_t dest_acc  = {0};
      35             : 
      36             :   /* Obtain the proof account pubkey by borrowing the proof account.
      37             :      https://github.com/anza-xyz/agave/blob/master/programs/zk-elgamal-proof/src/lib.rs#L143-L145 */
      38           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(ctx, ACC_IDX_PROOF, &proof_acc );
      39           0 :   *proof_pubkey = *proof_acc.acct->pubkey;
      40           0 :   fd_borrowed_account_drop( &proof_acc );
      41             : 
      42             :   /* Obtain the dest account pubkey by borrowing the dest account.
      43             :      https://github.com/anza-xyz/agave/blob/master/programs/zk-elgamal-proof/src/lib.rs#L146-L148*/
      44           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_DEST, &dest_acc );
      45           0 :   *dest_pubkey = *dest_acc.acct->pubkey;
      46           0 :   fd_borrowed_account_drop( &dest_acc );
      47             : 
      48           0 :   if( FD_UNLIKELY( fd_memeq( proof_pubkey, dest_pubkey, sizeof(fd_pubkey_t) ) ) ) {
      49           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
      50           0 :   }
      51             : 
      52             :   /* Re-borrow the proof account
      53             :      https://github.com/anza-xyz/agave/blob/master/programs/zk-elgamal-proof/src/lib.rs#L153-L154 */
      54           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(ctx, ACC_IDX_PROOF, &proof_acc );
      55             : 
      56             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L161-L162
      57             :       Note: data also contains context data, but we only need the initial 33 bytes. */
      58           0 :   if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &proof_acc ) < sizeof(fd_zksdk_proof_ctx_state_meta_t) ) ) {
      59           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      60           0 :   }
      61           0 :   fd_zksdk_proof_ctx_state_meta_t const * proof_ctx_state_meta = fd_type_pun_const( fd_borrowed_account_get_data( &proof_acc ) );
      62             : 
      63             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L155 */
      64           0 :   fd_pubkey_t const * expected_owner_addr = &proof_ctx_state_meta->ctx_state_authority;
      65             : 
      66             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L157-L159 */
      67           0 :   if( FD_UNLIKELY( !fd_memeq( owner_pubkey, expected_owner_addr, sizeof(fd_pubkey_t) ) ) ) {
      68           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
      69           0 :   }
      70             : 
      71             :   /* Re-borrow the dest account
      72             :      https://github.com/anza-xyz/agave/blob/v2.1.14/programs/zk-elgamal-proof/src/lib.rs#L162-L163 */
      73           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, ACC_IDX_DEST, &dest_acc );
      74             : 
      75             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L163-L166 */
      76           0 :   int err = 0;
      77           0 :   err = fd_borrowed_account_checked_add_lamports( &dest_acc, fd_borrowed_account_get_lamports( &proof_acc ) );
      78           0 :   if( FD_UNLIKELY( err ) ) {
      79           0 :     return err;
      80           0 :   }
      81           0 :   err = fd_borrowed_account_set_lamports( &proof_acc, 0UL );
      82           0 :   if( FD_UNLIKELY( err ) ) {
      83           0 :     return err;
      84           0 :   }
      85           0 :   err = fd_borrowed_account_set_data_length( &proof_acc, 0UL );
      86           0 :   if( FD_UNLIKELY( err ) ) {
      87           0 :     return err;
      88           0 :   }
      89           0 :   err = fd_borrowed_account_set_owner( &proof_acc, &fd_solana_system_program_id );
      90           0 :   if( FD_UNLIKELY( err ) ) {
      91           0 :     return err;
      92           0 :   }
      93             : 
      94           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
      95           0 : }
      96             : 
      97             : /* fd_zksdk_process_verify_proof is equivalent to process_verify_proof()
      98             :    and calls specific functions inside instructions/ to verify each
      99             :    individual ZKP.
     100             :    https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L32 */
     101             : int
     102          12 : fd_zksdk_process_verify_proof( fd_exec_instr_ctx_t * ctx ) {
     103          12 :   int err;
     104          12 :   uchar const * instr_data = ctx->instr->data;
     105          12 :   ulong instr_acc_cnt      = ctx->instr->acct_cnt;
     106          12 :   uchar instr_id = instr_data[0]; /* instr_data_sz already checked by the caller */
     107             : 
     108             :   /* ProofContextState "header" size, ie. 1 authority pubkey + 1 proof_type byte */
     109          12 : #define CTX_HEAD_SZ 33UL
     110             : 
     111             :   /* Aux memory buffer.
     112             :      When proof data is taken from ix data we can access it directly,
     113             :      but when it's taken from account data we need to copy it to release
     114             :      the borrow. The largest ZKP is for range_proof_u256.
     115             :      Moreover, when storing context to an account, we need to serialize
     116             :      the ProofContextState struct that has 33 bytes of header -- we include
     117             :      them here so we can do a single memcpy. */
     118          12 : #define MAX_SZ (sizeof(fd_zksdk_range_proof_u256_proof_t)+sizeof(fd_zksdk_batched_range_proof_context_t))
     119          12 :   uchar buffer[ CTX_HEAD_SZ+MAX_SZ ];
     120             : 
     121             :   /* Specific instruction function */
     122          12 :   int (*fd_zksdk_instr_verify_proof)( void const *, void const * ) = NULL;
     123          12 :   switch( instr_id ) {
     124           0 :   case FD_ZKSDK_INSTR_VERIFY_ZERO_CIPHERTEXT:
     125           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_zero_ciphertext;
     126           0 :     break;
     127           0 :   case FD_ZKSDK_INSTR_VERIFY_CIPHERTEXT_CIPHERTEXT_EQUALITY:
     128           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_ciphertext_ciphertext_equality;
     129           0 :     break;
     130           0 :   case FD_ZKSDK_INSTR_VERIFY_CIPHERTEXT_COMMITMENT_EQUALITY:
     131           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_ciphertext_commitment_equality;
     132           0 :     break;
     133          12 :   case FD_ZKSDK_INSTR_VERIFY_PUBKEY_VALIDITY:
     134          12 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_pubkey_validity;
     135          12 :     break;
     136           0 :   case FD_ZKSDK_INSTR_VERIFY_PERCENTAGE_WITH_CAP:
     137           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_percentage_with_cap;
     138           0 :     break;
     139           0 :   case FD_ZKSDK_INSTR_VERIFY_BATCHED_RANGE_PROOF_U64:
     140           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_batched_range_proof_u64;
     141           0 :     break;
     142           0 :   case FD_ZKSDK_INSTR_VERIFY_BATCHED_RANGE_PROOF_U128:
     143           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_batched_range_proof_u128;
     144           0 :     break;
     145           0 :   case FD_ZKSDK_INSTR_VERIFY_BATCHED_RANGE_PROOF_U256:
     146           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_batched_range_proof_u256;
     147           0 :     break;
     148           0 :   case FD_ZKSDK_INSTR_VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY:
     149           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity;
     150           0 :     break;
     151           0 :   case FD_ZKSDK_INSTR_VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY:
     152           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_batched_grouped_ciphertext_2_handles_validity;
     153           0 :     break;
     154           0 :   case FD_ZKSDK_INSTR_VERIFY_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY:
     155           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_grouped_ciphertext_3_handles_validity;
     156           0 :     break;
     157           0 :   case FD_ZKSDK_INSTR_VERIFY_BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY:
     158           0 :     fd_zksdk_instr_verify_proof = &fd_zksdk_instr_verify_proof_batched_grouped_ciphertext_3_handles_validity;
     159           0 :     break;
     160           0 :   default:
     161           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
     162          12 :   }
     163             : 
     164             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L42 */
     165          12 :   ushort        accessed_accounts = 0U;
     166          12 :   uchar const * context           = NULL;
     167             :   /* Note: instr_id is guaranteed to be valid, to access values in the arrays. */
     168          12 :   ulong context_sz = fd_zksdk_context_sz[instr_id];
     169          12 :   ulong proof_data_sz = context_sz + fd_zksdk_proof_sz[instr_id];
     170             : 
     171             :   /* if instruction data is exactly 5 bytes, then read proof from an account
     172             :      https://github.com/anza-xyz/agave/blob/v2.1.14/programs/zk-elgamal-proof/src/lib.rs#L46 */
     173          12 :   if( ctx->instr->data_sz == 5UL ) {
     174             :     /* Case 1. Proof data from account data. */
     175             : 
     176             :     /* Borrow the proof data account.
     177             :       https://github.com/anza-xyz/agave/blob/v2.1.14/programs/zk-elgamal-proof/src/lib.rs#L47-L48 */
     178           0 :     fd_guarded_borrowed_account_t proof_data_acc = {0};
     179           0 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0UL, &proof_data_acc );
     180             : 
     181             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L48 */
     182           0 :     accessed_accounts = 1U;
     183             : 
     184             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L50-L61
     185             :         Note: it doesn't look like the ref code can throw any error. */
     186           0 :     uint proof_data_offset = fd_uint_load_4_fast(&instr_data[1]);
     187             : 
     188             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L62-L65 */
     189           0 :     if( proof_data_offset+proof_data_sz > fd_borrowed_account_get_data_len( &proof_data_acc ) ) {
     190           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     191           0 :     }
     192           0 :     uchar const * proof_acc_data = fd_borrowed_account_get_data( &proof_data_acc );
     193           0 :     context = fd_memcpy( buffer+CTX_HEAD_SZ, &proof_acc_data[proof_data_offset], proof_data_sz );
     194          12 :   } else {
     195             :     /* Case 2. Proof data from ix data. */
     196             : 
     197             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L78-L82
     198             :        Note: instr_id is guaranteed to be valid, to access values in the arrays. */
     199          12 :     if (ctx->instr->data_sz != 1 + proof_data_sz) {
     200           3 :       fd_log_collector_msg_literal( ctx, "invalid proof data" );
     201           3 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
     202           3 :     }
     203           9 :     context = instr_data + 1;
     204           9 :   }
     205             : 
     206             :   /* Verify individual ZKP
     207             :      https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L83-L86 */
     208           9 :   void const * proof = context + fd_zksdk_context_sz[instr_id];
     209           9 :   err = (*fd_zksdk_instr_verify_proof)( context, proof );
     210           9 :   if( FD_UNLIKELY( err ) ) {
     211             :     //TODO: full log, including err
     212           3 :     fd_log_collector_msg_literal( ctx, "proof_verification failed" );
     213           3 :     return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
     214           3 :   }
     215             : 
     216             :   /* Create context state if accounts are provided with the instruction
     217             :      https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L92 */
     218           6 :   if( instr_acc_cnt > accessed_accounts ) {
     219           0 :     fd_pubkey_t context_state_authority[1];
     220             : 
     221             :     /* Obtain the context_state_authority by borrowing the account temporarily in a local scope.
     222             :        https://github.com/anza-xyz/agave/blob/v2.1.14/programs/zk-elgamal-proof/src/lib.rs#L94-L99 */
     223           0 :     do {
     224           0 :       fd_guarded_borrowed_account_t _acc = {0};
     225           0 :       FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, (ushort)(accessed_accounts+1), &_acc );
     226           0 :       *context_state_authority = *_acc.acct->pubkey;
     227           0 :     } while(0);
     228             : 
     229             :     /* Borrow the proof context account
     230             :        https://github.com/anza-xyz/agave/blob/v2.1.14/programs/zk-elgamal-proof/src/lib.rs#L101-L102 */
     231           0 :     fd_guarded_borrowed_account_t proof_context_acc = {0};
     232           0 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, accessed_accounts, &proof_context_acc );
     233             : 
     234             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L103-L105 */
     235           0 :     if( FD_UNLIKELY( !fd_memeq( fd_borrowed_account_get_owner( &proof_context_acc ), &fd_solana_zk_elgamal_proof_program_id, sizeof(fd_pubkey_t) ) ) ) {
     236           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
     237           0 :     }
     238             : 
     239             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L107-L112 */
     240           0 :     if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &proof_context_acc ) >= CTX_HEAD_SZ && fd_borrowed_account_get_data( &proof_context_acc )[32] != 0 ) ) {
     241           0 :       return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
     242           0 :     }
     243             : 
     244             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L114-L115
     245             :         Note: nothing to do. */
     246             : 
     247             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L117-L119 */
     248           0 :     ulong context_data_sx = CTX_HEAD_SZ + context_sz;
     249           0 :     if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &proof_context_acc ) != context_data_sx ) ) {
     250           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     251           0 :     }
     252             : 
     253             :     /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/zk-elgamal-proof/src/lib.rs#L121 */
     254           0 :     fd_memcpy( buffer, context_state_authority, sizeof(fd_pubkey_t) ); // buffer[0..31]
     255           0 :     buffer[ 32 ] = instr_id;                                           // buffer[32]
     256           0 :     if( ctx->instr->data_sz != 5UL ) {                                  // buffer[33..]
     257           0 :       fd_memcpy( buffer+CTX_HEAD_SZ, context, context_sz );
     258           0 :     }
     259           0 :     err = fd_borrowed_account_set_data_from_slice( &proof_context_acc, buffer, context_data_sx );
     260           0 :     if( FD_UNLIKELY( err ) ) {
     261           0 :       return err;
     262           0 :     }
     263           0 :   }
     264             : 
     265           6 :   return FD_EXECUTOR_INSTR_SUCCESS;
     266           6 : }

Generated by: LCOV version 1.14