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 : 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 9 : uchar scalars[ 12 * 32 ];
110 9 : fd_ristretto255_point_t points[12];
111 9 : fd_ristretto255_point_t y0[1];
112 9 : fd_ristretto255_point_t res[1];
113 :
114 9 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zr )==NULL ) ) {
115 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
116 0 : }
117 9 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->zx )==NULL ) ) {
118 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
119 0 : }
120 :
121 9 : fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_G );
122 9 : fd_ristretto255_point_set( &points[1], fd_zksdk_basepoint_H );
123 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( y0, proof->y0 )==NULL ) ) {
124 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
125 0 : }
126 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], proof->y1 )==NULL ) ) {
127 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
128 0 : }
129 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], proof->y2 )==NULL ) ) {
130 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
131 0 : }
132 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], pubkey1 )==NULL ) ) {
133 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
134 0 : }
135 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[5], comm )==NULL ) ) {
136 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
137 0 : }
138 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[6], handle1 )==NULL ) ) {
139 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
140 0 : }
141 :
142 9 : ulong idx = 7;
143 9 : if( batched ) {
144 0 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], comm_hi )==NULL ) ) {
145 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
146 0 : }
147 0 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle1_hi )==NULL ) ) {
148 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
149 0 : }
150 0 : }
151 :
152 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], pubkey2 )==NULL ) ) {
153 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
154 0 : }
155 9 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2 )==NULL ) ) {
156 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
157 0 : }
158 :
159 9 : if( batched ) {
160 0 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[idx++], handle2_hi )==NULL ) ) {
161 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
162 0 : }
163 0 : }
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 9 : 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 9 : int val = FD_TRANSCRIPT_SUCCESS;
172 9 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_0"), proof->y0);
173 9 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_1"), proof->y1);
174 9 : if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) {
175 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
176 0 : }
177 : /* Y_2 can be an all zero point if the pubkey2 is all zero */
178 9 : fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_2"), proof->y2);
179 :
180 9 : uchar c[ 32 ];
181 9 : uchar w[ 32 ];
182 9 : fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") );
183 :
184 9 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_r"), proof->zr );
185 9 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z_x"), proof->zx );
186 :
187 9 : 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 9 : fd_curve25519_scalar_set( &scalars[ 0*32 ], proof->zx ); // z_x
194 9 : fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->zr ); // z_r
195 9 : fd_curve25519_scalar_neg( &scalars[ 2*32 ], w ); // -w
196 9 : fd_curve25519_scalar_mul( &scalars[ 3*32 ], &scalars[ 2*32 ], w ); // -w^2
197 9 : fd_curve25519_scalar_mul( &scalars[ 4*32 ], proof->zr, w ); // z_r w
198 9 : fd_curve25519_scalar_neg( &scalars[ 5*32 ], c ); // -c
199 9 : fd_curve25519_scalar_mul( &scalars[ 6*32 ], &scalars[ 5*32 ], w ); // -c w
200 9 : idx = 7;
201 9 : if( batched ) {
202 0 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 5*32 ], challenge_t ); // -c t
203 0 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], challenge_t ); // -c w t
204 0 : }
205 9 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 4*32 ], w ); // z_r w^2
206 9 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 6*32 ], w ); // -c w^2
207 9 : if( batched ) {
208 0 : fd_curve25519_scalar_mul( &scalars[ (idx++)*32 ], &scalars[ 8*32 ], w ); // -c w^2 t
209 0 : }
210 :
211 : /* Compute the final MSM */
212 9 : 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 9 : if( FD_LIKELY( fd_ristretto255_point_eq( res, y0 ) ) ) {
216 3 : return FD_ZKSDK_VERIFY_PROOF_SUCCESS;
217 3 : }
218 6 : return FD_ZKSDK_VERIFY_PROOF_ERROR;
219 9 : }
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 9 : fd_zksdk_instr_verify_proof_grouped_ciphertext_2_handles_validity( void const * _context, void const * _proof ) {
224 9 : fd_zksdk_transcript_t transcript[1];
225 9 : fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("grouped-ciphertext-validity-2-handles-instruction") );
226 :
227 9 : fd_zksdk_grp_ciph_2h_val_context_t const * context = _context;
228 9 : fd_zksdk_grp_ciph_2h_val_proof_t const * proof = _proof;
229 9 : return fd_zksdk_verify_proof_grouped_ciphertext_2_handles_validity(
230 9 : proof,
231 9 : context->pubkey1,
232 9 : context->pubkey2,
233 9 : context->grouped_ciphertext,
234 9 : transcript
235 9 : );
236 9 : }
|