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