LCOV - code coverage report
Current view: top level - flamenco/runtime/program/zksdk - fd_zksdk.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 156 162 96.3 %
Date: 2024-11-13 11:58:15 Functions: 2 2 100.0 %

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

Generated by: LCOV version 1.14