Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
ligero_verifier.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_
16#define PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_
17
18#include <stddef.h>
19
20#include <array>
21#include <vector>
22
23#include "algebra/blas.h"
24#include "ligero/ligero_param.h"
25#include "ligero/ligero_transcript.h"
26#include "merkle/merkle_commitment.h"
27#include "random/transcript.h"
28#include "util/crypto.h"
29
30namespace proofs {
31template <class Field, class InterpolatorFactory>
33 using Elt = typename Field::Elt;
34
35 public:
36 static void receive_commitment(const LigeroCommitment<Field>& commitment,
37 Transcript& ts) {
38 // P -> V
39 LigeroTranscript<Field>::write_commitment(commitment, ts);
40 }
41
42 static bool verify(const char** why, const LigeroParam<Field>& p,
43 const LigeroCommitment<Field>& commitment,
44 const LigeroProof<Field>& proof, Transcript& ts, size_t nl,
45 size_t nllterm,
46 const LigeroLinearConstraint<Field> llterm[/*nllterm*/],
47 const LigeroHash& hash_of_llterm, const Elt b[/*nl*/],
48 const LigeroQuadraticConstraint lqc[/*nq*/],
49 const InterpolatorFactory& interpolator, const Field& F) {
50 if (why == nullptr) {
51 return false;
52 }
53
54 std::vector<Elt> u_ldt(p.nwqrow);
55 std::vector<Elt> alphal(nl);
56 std::vector<std::array<Elt, 3>> alphaq(p.nq);
57 std::vector<Elt> u_quad(p.nqtriples);
58 std::vector<size_t> idx(p.nreq);
59
60 // Replay the protocol first in order to compute all the
61 // challenges. In particular, we need IDX before we can do
62 // anything useful.
63
64 // P -> V
65 ts.write(hash_of_llterm.bytes, hash_of_llterm.kLength);
66
67 // V -> P
68 LigeroTranscript<Field>::gen_uldt(&u_ldt[0], p, ts, F);
69
70 // V -> P
71 LigeroTranscript<Field>::gen_alphal(nl, &alphal[0], ts, F);
72 LigeroTranscript<Field>::gen_alphaq(&alphaq[0], p, ts, F);
73
74 // V -> P
75 LigeroTranscript<Field>::gen_uquad(&u_quad[0], p, ts, F);
76
77 // P -> V
78 ts.write(&proof.y_ldt[0], 1, p.block, F);
79 ts.write(&proof.y_dot[0], 1, p.dblock, F);
80 ts.write(&proof.y_quad_0[0], 1, p.r, F);
81 ts.write(&proof.y_quad_2[0], 1, p.dblock - p.block, F);
82
83 // V -> P
84 LigeroTranscript<Field>::gen_idx(&idx[0], p, ts, F);
85
86 if (!merkle_check(p, commitment, proof, &idx[0], F)) {
87 *why = "merkle_check failed";
88 return false;
89 }
90
91 if (!low_degree_check(p, proof, &idx[0], &u_ldt[0], interpolator, F)) {
92 *why = "low_degree_check failed";
93 return false;
94 }
95
96 {
97 // linear check
98 std::vector<Elt> A(p.nwqrow * p.w);
99
100 LigeroCommon<Field>::inner_product_vector(&A[0], p, nl, nllterm, llterm,
101 &alphal[0], lqc, &alphaq[0], F);
102
103 if (!dot_check(p, proof, &idx[0], &A[0], interpolator, F)) {
104 *why = "dot_check failed";
105 return false;
106 }
107
108 // check the putative value of the inner product
109 Elt want_dot = Blas<Field>::dot(nl, b, 1, &alphal[0], 1, F);
110 Elt proof_dot = Blas<Field>::dot1(p.w, &proof.y_dot[p.r], 1, F);
111 if (want_dot != proof_dot) {
112 *why = "wrong dot product";
113 return false;
114 }
115 }
116
117 if (!quadratic_check(p, proof, &idx[0], &u_quad[0], interpolator, F)) {
118 *why = "quadratic_check failed";
119 return false;
120 }
121
122 *why = "ok";
123 return true;
124 }
125
126 private:
127 static void interpolate_req_columns(Elt yp[/*nreq*/],
128 const LigeroParam<Field>& p, size_t ylen,
129 const Elt y[/*ylen*/],
130 const size_t idx[/*nreq*/],
131 const InterpolatorFactory& interpolator,
132 const Field& F) {
133 const auto interpy = interpolator.make(ylen, p.block_enc);
134 std::vector<Elt> yext(p.block_enc);
135 Blas<Field>::copy(ylen, &yext[0], 1, y, 1);
136 interpy->interpolate(&yext[0]);
137 Blas<Field>::gather(p.nreq, &yp[0], &yext[p.dblock], idx);
138 }
139
140 static bool merkle_check(const LigeroParam<Field>& p,
141 const LigeroCommitment<Field>& commitment,
142 const LigeroProof<Field>& proof,
143 const size_t idx[/*nreq*/], const Field& F) {
144 auto updhash = [&](size_t r, SHA256& sha) {
145 LigeroCommon<Field>::column_hash(p.nrow, &proof.req_at(0, r), p.nreq, sha,
146 F);
147 };
148
149 return MerkleCommitmentVerifier::verify(p.block_enc - p.dblock,
150 commitment.root, proof.merkle, idx,
151 p.nreq, updhash);
152 }
153
154 static bool low_degree_check(const LigeroParam<Field>& p,
155 const LigeroProof<Field>& proof,
156 const size_t idx[/*nreq*/],
157 const Elt u_ldt[/*nrow*/],
158 const InterpolatorFactory& interpolator,
159 const Field& F) {
160 std::vector<Elt> yc(p.nreq);
161
162 // the ILDT blinding row with coefficient 1
163 Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.ildt, 0), 1);
164
165 // all remaining rows with coefficient u_ldt[]
166 for (size_t i = 0; i < p.nwqrow; ++i) {
167 Blas<Field>::axpy(p.nreq, &yc[0], 1, u_ldt[i], &proof.req_at(i + p.iw, 0),
168 1, F);
169 }
170
171 std::vector<Elt> yp(p.nreq);
172 interpolate_req_columns(&yp[0], p, p.block, &proof.y_ldt[0], idx,
173 interpolator, F);
174
175 if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
176 return false;
177 }
178
179 return true;
180 }
181
182 static bool dot_check(const LigeroParam<Field>& p,
183 const LigeroProof<Field>& proof,
184 const size_t idx[/*nreq*/], const Elt A[/*nwqrow, w*/],
185 const InterpolatorFactory& interpolator,
186 const Field& F) {
187 std::vector<Elt> yc(p.nreq);
188
189 // the IDOT blinding row with coefficient 1
190 Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.idot, 0), 1);
191
192 {
193 const auto interpA = interpolator.make(p.block, p.block_enc);
194
195 std::vector<Elt> Aext(p.block_enc);
196 std::vector<Elt> Areq(p.nreq);
197
198 for (size_t i = 0; i < p.nwqrow; ++i) {
199 LigeroCommon<Field>::layout_Aext(&Aext[0], p, i, &A[0], F);
200 interpA->interpolate(&Aext[0]);
201 Blas<Field>::gather(p.nreq, &Areq[0], &Aext[p.dblock], idx);
202
203 // Accumulate z += A[j] \otimes W[j].
204 Blas<Field>::vaxpy(p.nreq, &yc[0], 1, &Areq[0], 1,
205 &proof.req_at(i + p.iw, 0), 1, F);
206 }
207 }
208
209 std::vector<Elt> yp(p.nreq);
210 interpolate_req_columns(&yp[0], p, p.dblock, &proof.y_dot[0], idx,
211 interpolator, F);
212
213 if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
214 return false;
215 }
216 return true;
217 }
218
219 static bool quadratic_check(const LigeroParam<Field>& p,
220 const LigeroProof<Field>& proof,
221 const size_t idx[/*nreq*/],
222 const Elt u_quad[/*nqtriples*/],
223 const InterpolatorFactory& interpolator,
224 const Field& F) {
225 std::vector<Elt> yc(p.nreq);
226
227 // the IQUAD blinding row with coefficient 1
228 Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.iquad, 0), 1);
229
230 {
231 std::vector<Elt> tmp(p.nreq);
232 size_t iqx = p.iq;
233 size_t iqy = iqx + p.nqtriples;
234 size_t iqz = iqy + p.nqtriples;
235
236 // all quadratic triples with coefficient u_ldt[]
237 for (size_t i = 0; i < p.nqtriples; ++i) {
238 // yc += u_quad[i] * (z[i] - x[i] * y[i])
239
240 // tmp = z[i]
241 Blas<Field>::copy(p.nreq, &tmp[0], 1, &proof.req_at(iqz + i, 0), 1);
242
243 // tmp -= x[i] \otimes y[i]
244 Blas<Field>::vymax(p.nreq, &tmp[0], 1, &proof.req_at(iqx + i, 0), 1,
245 &proof.req_at(iqy + i, 0), 1, F);
246
247 // yc += u_quad[i] * tmp
248 Blas<Field>::axpy(p.nreq, &yc[0], 1, u_quad[i], &tmp[0], 1, F);
249 }
250 }
251
252 // reconstruct y_quad from the two parts in the proof
253 std::vector<Elt> yquad(p.dblock);
254 Blas<Field>::copy(p.r, &yquad[0], 1, &proof.y_quad_0[0], 1);
255 Blas<Field>::clear(p.w, &yquad[p.r], 1, F);
256 Blas<Field>::copy(p.dblock - p.block, &yquad[p.block], 1,
257 &proof.y_quad_2[0], 1);
258
259 // interpolate y_quad at the opened columns
260 std::vector<Elt> yp(p.nreq);
261 interpolate_req_columns(&yp[0], p, p.dblock, &yquad[0], idx, interpolator,
262 F);
263
264 if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
265 return false;
266 }
267 return true;
268 }
269};
270} // namespace proofs
271
272#endif // PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_
Definition ligero_verifier.h:32
Definition crypto.h:40
Definition transcript.h:65
Definition gf2_128.h:63
Definition ligero_param.h:294
Definition ligero_param.h:426
Definition ligero_param.h:344
Definition ligero_param.h:117
Definition ligero_param.h:299
Definition ligero_param.h:352