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

Generated by: LCOV version 1.14