Line data Source code
1 : #include "../fd_zksdk_private.h"
2 :
3 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L161 */
4 : static inline void
5 : grouped_ciphertext_validity_hash_context( fd_zksdk_transcript_t * transcript,
6 : uchar const pubkey1 [ 32 ],
7 : uchar const pubkey2 [ 32 ],
8 208 : grp_ciph_2h_t const * grouped_ciphertext ) {
9 208 : fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("first-pubkey"), pubkey1 );
10 208 : fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("second-pubkey"), pubkey2 );
11 208 : fd_zksdk_transcript_append_message( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext"), (uchar *)grouped_ciphertext, sizeof(grp_ciph_2h_t) );
12 208 : }
13 :
14 : static inline int
15 : fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity( fd_zksdk_grp_ciph_2h_val_proof_t const * proof,
16 : uchar const pubkey1 [ 32 ],
17 : uchar const pubkey2 [ 32 ],
18 : grp_ciph_2h_t const * grouped_ciphertext,
19 211 : fd_zksdk_transcript_t * transcript ) {
20 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L155-L159 */
21 211 : if( FD_UNLIKELY( fd_memeq( pubkey1, fd_ristretto255_compressed_zero, 32 )
22 211 : || fd_memeq( grouped_ciphertext->commitment, fd_ristretto255_compressed_zero, 32 ) ) ) {
23 3 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
24 3 : }
25 :
26 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L161-L166 */
27 208 : grouped_ciphertext_validity_hash_context( transcript, pubkey1, pubkey2, grouped_ciphertext );
28 :
29 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L167 */
30 208 : return fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
31 208 : proof,
32 208 : pubkey1,
33 208 : pubkey2,
34 208 : grouped_ciphertext->commitment,
35 208 : grouped_ciphertext->handles[0].handle,
36 208 : grouped_ciphertext->handles[1].handle,
37 208 : NULL,
38 208 : NULL,
39 208 : NULL,
40 208 : NULL,
41 208 : 0,
42 208 : transcript
43 208 : );
44 211 : }
45 :
46 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L170
47 : In Agave, the verify_direct() is a method of the NON-batched proof.
48 : The batched proof is converted into a non-batched proof with 3 mul.
49 : However, verify_direct() is doing a MSM so we can embed the 3 mul
50 : as part of it.
51 : So, in Firedancer verify_direct optionally supports a batched
52 : proof and computes a single, adjusted MSM. */
53 : int
54 : fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
55 : fd_zksdk_grp_ciph_2h_val_proof_t const * proof,
56 : uchar const pubkey1 [ 32 ],
57 : uchar const pubkey2 [ 32 ],
58 : uchar const comm [ 32 ],
59 : uchar const handle1 [ 32 ],
60 : uchar const handle2 [ 32 ],
61 : uchar const comm_hi [ 32 ],
62 : uchar const handle1_hi [ 32 ],
63 : uchar const handle2_hi [ 32 ],
64 : uchar const challenge_t[ 32 ],
65 : int const batched,
66 417 : fd_zksdk_transcript_t * transcript ) {
67 : /*
68 : We need to verify the 3 following equivalences.
69 : Instead of verifying them one by one, it's more efficient to pack
70 : them up in a single MSM (and to do so we have to mul by 1, w, w^2).
71 :
72 : ( z_r H + z_x G =?= c C + Y_0 ) * 1
73 : ( z_r pub1 =?= c h1 + Y_1 ) * w
74 : ( z_r pub2 =?= c h2 + Y_2 ) * w^2
75 :
76 : When batched==false, C, h1, h2 are given and C_hi, h1_hi, h2_hi are NULL.
77 : When batched==true, they are computed as C = C_lo + t C_hi.
78 :
79 : An honest proof with identity pubkey2 has identity Y_2 and handles.
80 : Verification must not assume this for untrusted proofs: keep those
81 : terms in the MSM even when pubkey2 is identity.
82 :
83 : Because of batched, the length of the MSM varies between 9 and 12.
84 : Points/scalars 7-8 (if batched) and 11 (if batched) are only
85 : included when required.
86 :
87 : We store points and scalars in the following arrays:
88 :
89 : non-batched (9 points): batched (12 points):
90 :
91 : points scalars points scalars
92 : 0 G z_x 0 G z_x
93 : 1 H z_r 1 H z_r
94 : 2 Y_1 -w 2 Y_1 -w
95 : 3 Y_2 -w^2 3 Y_2 -w^2
96 : 4 pub1 z_r w 4 pub1 z_r w
97 : 5 C -c 5 C -c
98 : 6 h1 -c w 6 h1 -c w
99 : 7 pub2 z_r w^2 7 C_hi -c t
100 : 8 h2 -c w^2 8 h1_hi -c w t
101 : ----------------------- MSM 9 pub2 z_r w^2
102 : Y_0 10 h2 -c w^2
103 : 11 h2_hi -c w^2 t
104 : ----------------------- MSM
105 : Y_0
106 : */
107 :
108 : /* Validate all inputs */
109 417 : uchar scalars[ 12 * 32 ];
110 417 : fd_ristretto255_point_t points[12];
111 417 : fd_ristretto255_point_t y0[1];
112 417 : fd_ristretto255_point_t res[1];
113 :
114 417 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zr )==NULL ) ) {
115 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
116 2 : }
117 415 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zx )==NULL ) ) {
118 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
119 2 : }
120 :
121 413 : fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_G );
122 413 : fd_ristretto255_point_set( &points[1], fd_zksdk_basepoint_H );
123 413 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( y0, proof->y0 )==NULL ) ) {
124 3 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
125 3 : }
126 410 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], proof->y1 )==NULL ) ) {
127 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
128 2 : }
129 408 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], proof->y2 )==NULL ) ) {
130 1 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
131 1 : }
132 407 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], pubkey1 )==NULL ) ) {
133 4 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
134 4 : }
135 403 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[5], comm )==NULL ) ) {
136 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
137 2 : }
138 401 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[6], handle1 )==NULL ) ) {
139 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
140 2 : }
141 :
142 399 : ulong idx = 7;
143 399 : if( batched ) {
144 203 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], comm_hi )==NULL ) ) {
145 1 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
146 1 : }
147 202 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle1_hi )==NULL ) ) {
148 1 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
149 1 : }
150 202 : }
151 :
152 397 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], pubkey2 )==NULL ) ) {
153 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
154 2 : }
155 395 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2 )==NULL ) ) {
156 1 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
157 1 : }
158 :
159 394 : if( batched ) {
160 199 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2_hi )==NULL ) ) {
161 1 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
162 1 : }
163 199 : }
164 :
165 : /* Finalize transcript and extract challenges */
166 :
167 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L177 */
168 393 : fd_zksdk_transcript_domsep_grp_ciph_val_proof( transcript, 2 );
169 :
170 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L179-L189 */
171 393 : int val = FD_TRANSCRIPT_SUCCESS;
172 393 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_0"), proof->y0);
173 393 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_1"), proof->y1);
174 393 : if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
175 3 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
176 3 : }
177 : /* Y_2 can be an all zero point if the pubkey2 is all zero */
178 390 : fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_2"), proof->y2);
179 :
180 390 : uchar c[ 32 ];
181 390 : uchar w[ 32 ];
182 390 : fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
183 :
184 390 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_r"), proof->zr );
185 390 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_x"), proof->zx );
186 :
187 390 : fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") );
188 :
189 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L190-L244
190 : Note: we use a slightly different MSM but they're equivalent. */
191 :
192 : /* Compute scalars */
193 390 : fd_curve25519_scalar_set( &scalars[ 0*32 ], proof->zx ); // z_x
194 390 : fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->zr ); // z_r
195 390 : fd_curve25519_scalar_neg( &scalars[ 2*32 ], w ); // -w
196 390 : fd_curve25519_scalar_mul( &scalars[ 3*32 ], &scalars[ 2*32 ], w ); // -w^2
197 390 : fd_curve25519_scalar_mul( &scalars[ 4*32 ], proof->zr, w ); // z_r w
198 390 : fd_curve25519_scalar_neg( &scalars[ 5*32 ], c ); // -c
199 390 : fd_curve25519_scalar_mul( &scalars[ 6*32 ], &scalars[ 5*32 ], w ); // -c w
200 390 : idx = 7;
201 390 : if( batched ) {
202 197 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 5*32 ], challenge_t ); // -c t
203 197 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], challenge_t ); // -c w t
204 197 : }
205 390 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 4*32 ], w ); // z_r w^2
206 390 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], w ); // -c w^2
207 390 : if( batched ) {
208 197 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 8*32 ], w ); // -c w^2 t
209 197 : }
210 :
211 : /* Compute the final MSM */
212 390 : fd_ristretto255_multi_scalar_mul( res, scalars, points, idx );
213 :
214 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs#L246-L250 */
215 390 : if( FD_LIKELY( fd_ristretto255_point_eq( res, y0 ) ) ) {
216 378 : return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
217 378 : }
218 12 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
219 390 : }
220 :
221 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs#L109 */
222 : int
223 211 : fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity( void const * _context, void const * _proof ) {
224 211 : fd_zksdk_transcript_t transcript[1];
225 211 : fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext-validity-2-handles-instruction") );
226 :
227 211 : fd_zksdk_grp_ciph_2h_val_context_t const * context = _context;
228 211 : fd_zksdk_grp_ciph_2h_val_proof_t const * proof = _proof;
229 211 : return fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity(
230 211 : proof,
231 211 : context->pubkey1,
232 211 : context->pubkey2,
233 211 : context->grouped_ciphertext,
234 211 : transcript
235 211 : );
236 211 : }
|