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 6 : grp_ciph_2h_t const * grouped_ciphertext ) {
9 6 : fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("first-pubkey"), pubkey1 );
10 6 : fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("second-pubkey"), pubkey2 );
11 6 : fd_zksdk_transcript_append_message( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext"), (uchar *)grouped_ciphertext, sizeof(grp_ciph_2h_t) );
12 6 : }
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 6 : 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 6 : if( FD_UNLIKELY( fd_memeq( pubkey1, fd_ristretto255_compressed_zero, 32 )
22 6 : || fd_memeq( grouped_ciphertext->commitment, fd_ristretto255_compressed_zero, 32 ) ) ) {
23 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
24 0 : }
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 6 : 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 6 : return fd_zksdk_verify_proof_direct_grouped_ciphertext_2_handles_validity(
31 6 : proof,
32 6 : pubkey1,
33 6 : pubkey2,
34 6 : grouped_ciphertext->commitment,
35 6 : grouped_ciphertext->handles[0].handle,
36 6 : grouped_ciphertext->handles[1].handle,
37 6 : NULL,
38 6 : NULL,
39 6 : NULL,
40 6 : NULL,
41 6 : 0,
42 6 : transcript
43 6 : );
44 6 : }
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 12 : 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 : When pubkey2 is 0, also proof->y2, handle2 and handle2_hi should be 0.
80 :
81 : Because of batched and pubkey2_not_zero, the length of the MSM varies
82 : between 6 and 12.
83 : Points/scalars from 7 to 12 are only computed when required.
84 :
85 : We store points and scalars in the following arrays:
86 :
87 : points scalars
88 : 0 G z_x
89 : 1 H z_r
90 : 2 Y_1 -w
91 : 3 Y_2 -w^2
92 : 4 pub1 z_r w
93 : 5 C -c
94 : 6 h1 -c w
95 : 7 C_hi -c t (if batched)
96 : 8 h1_hi -c w t (if batched)
97 : 9 pub2 z_r w^2 (if pubkey2_not_zero)
98 : 10 h2 -c w^2 (if pubkey2_not_zero)
99 : 11 h2_hi -c w^2 t (if batched && pubkey2_not_zero)
100 : ----------------------- MSM
101 : Y_0
102 : */
103 :
104 : /* Validate all inputs */
105 12 : uchar scalars[ 12 * 32 ];
106 12 : fd_ristretto255_point_t points[12];
107 12 : fd_ristretto255_point_t y0[1];
108 12 : fd_ristretto255_point_t res[1];
109 :
110 12 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zr )==NULL ) ) {
111 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
112 0 : }
113 12 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zx )==NULL ) ) {
114 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
115 0 : }
116 :
117 12 : fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_G );
118 12 : fd_ristretto255_point_set( &points[1], fd_zksdk_basepoint_H );
119 12 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( y0, proof->y0 )==NULL ) ) {
120 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
121 0 : }
122 12 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], proof->y1 )==NULL ) ) {
123 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
124 0 : }
125 12 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], proof->y2 )==NULL ) ) {
126 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
127 0 : }
128 12 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], pubkey1 )==NULL ) ) {
129 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
130 2 : }
131 10 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[5], comm )==NULL ) ) {
132 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
133 0 : }
134 10 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[6], handle1 )==NULL ) ) {
135 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
136 0 : }
137 :
138 10 : ulong idx = 7;
139 10 : if( batched ) {
140 5 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], comm_hi )==NULL ) ) {
141 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
142 0 : }
143 5 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle1_hi )==NULL ) ) {
144 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
145 0 : }
146 5 : }
147 :
148 10 : int pubkey2_not_zero = !fd_memeq( pubkey2, fd_ristretto255_compressed_zero, 32 );
149 10 : if( pubkey2_not_zero ) {
150 10 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], pubkey2 )==NULL ) ) {
151 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
152 0 : }
153 10 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2 )==NULL ) ) {
154 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
155 0 : }
156 10 : }
157 :
158 10 : if( batched && pubkey2_not_zero ) {
159 5 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2_hi )==NULL ) ) {
160 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
161 0 : }
162 5 : }
163 :
164 : /* Finalize transcript and extract challenges */
165 :
166 : /* 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 */
167 10 : fd_zksdk_transcript_domsep_grp_ciph_val_proof( transcript, 2 );
168 :
169 : /* 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 */
170 10 : int val = FD_TRANSCRIPT_SUCCESS;
171 10 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_0"), proof->y0);
172 10 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_1"), proof->y1);
173 10 : if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
174 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
175 0 : }
176 : /* Y_2 can be an all zero point if the pubkey2 is all zero */
177 10 : fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_2"), proof->y2);
178 :
179 10 : uchar c[ 32 ];
180 10 : uchar w[ 32 ];
181 10 : fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
182 :
183 10 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_x"), proof->zx );
184 10 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_r"), proof->zr );
185 :
186 10 : fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") );
187 :
188 : /* 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
189 : Note: we use a slightly different MSM but they're equivalent. */
190 :
191 : /* Compute scalars */
192 10 : fd_curve25519_scalar_set( &scalars[ 0*32 ], proof->zx ); // z_x
193 10 : fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->zr ); // z_r
194 10 : fd_curve25519_scalar_neg( &scalars[ 2*32 ], w ); // -w
195 10 : fd_curve25519_scalar_mul( &scalars[ 3*32 ], &scalars[ 2*32 ], w ); // -w^2
196 10 : fd_curve25519_scalar_mul( &scalars[ 4*32 ], proof->zr, w ); // z_r w
197 10 : fd_curve25519_scalar_neg( &scalars[ 5*32 ], c ); // -c
198 10 : fd_curve25519_scalar_mul( &scalars[ 6*32 ], &scalars[ 5*32 ], w ); // -c w
199 10 : idx = 7;
200 10 : if( batched ) {
201 5 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 5*32 ], challenge_t ); // -c t
202 5 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], challenge_t ); // -c w t
203 5 : }
204 10 : if( pubkey2_not_zero ) {
205 10 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 4*32 ], w ); // z_r w^2
206 10 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], w ); // -c w^2
207 10 : }
208 10 : if( batched && pubkey2_not_zero ) {
209 5 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 8*32 ], w ); // -c w^2 t
210 5 : }
211 :
212 : /* Compute the final MSM */
213 10 : fd_ristretto255_multi_scalar_mul( res, scalars, points, idx );
214 :
215 : /* 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 */
216 10 : if( FD_LIKELY( fd_ristretto255_point_eq( res, y0 ) ) ) {
217 8 : return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
218 8 : }
219 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
220 10 : }
221 :
222 : /* 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 */
223 : int
224 6 : fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity( void const * _context, void const * _proof ) {
225 6 : fd_zksdk_transcript_t transcript[1];
226 6 : fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext-validity-2-handles-instruction") );
227 :
228 6 : fd_zksdk_grp_ciph_2h_val_context_t const * context = _context;
229 6 : fd_zksdk_grp_ciph_2h_val_proof_t const * proof = _proof;
230 6 : return fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity(
231 6 : proof,
232 6 : context->pubkey1,
233 6 : context->pubkey2,
234 6 : context->grouped_ciphertext,
235 6 : transcript
236 6 : );
237 6 : }
|