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