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 : }