Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
verifier_layers.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_SUMCHECK_VERIFIER_LAYERS_H_
16#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_
17
18#include <stddef.h>
19
20#include <cstddef>
21#include <memory>
22
23#include "arrays/affine.h"
24#include "arrays/dense.h"
25#include "arrays/eq.h"
26#include "sumcheck/circuit.h"
27#include "sumcheck/quad.h"
28#include "sumcheck/transcript_sumcheck.h"
29
30namespace proofs {
31// Sumcheck verifier that only verifies the layers.
32// Derived classes are responsible for verifying the
33// input binding, either directly or through a commitment.
34template <class Field>
35class VerifierLayers {
36 public:
37 typedef typename Quad<Field>::index_t index_t;
38 using Elt = typename Field::Elt;
39
40 struct claims {
41 corner_t nv;
42 size_t logv;
43 Elt claim[2];
44 const Elt* q;
45 const Elt* g[2];
46 };
47 // Verify all the circuit layers, returning claims on the inputs in
48 // CL. The caller is responsible to verify the claims, either via
49 // direct check or polynomial commitment.
50 static bool circuit(const char** why, claims* cl,
51 const Circuit<Field>* CIRCUIT, const Proof<Field>* PROOF,
52 Challenge<Field>* CH, std::unique_ptr<Dense<Field>> V,
53 TranscriptSumcheck<Field>& ts, const Field& F) {
54 if (why == nullptr || cl == nullptr || CIRCUIT == nullptr ||
55 PROOF == nullptr || CH == nullptr) {
56 return false;
57 }
58 *why = "ok";
59
60 Elt claimV;
61 ts.begin_circuit(CH->q, CH->g);
62
63 if (V->n1_ == 1 && V->n0_ == 1 && V->v_[0] == F.zero()) {
64 // special case of all-zero binding
65 claimV = F.zero();
66 } else {
67 const desire desires[2] = {
68 {V->n1_ == CIRCUIT->nv, "V->n1_ != CIRCUIT->nv"},
69 {V->n0_ == CIRCUIT->nc, "V->n0_ != CIRCUIT->nc"},
70 };
71
72 if (!check(why, 2, desires)) {
73 return false;
74 }
75
76 // initial claim on V[G, Q] for the output V
77 V->bind_all(CIRCUIT->logc, CH->q, F);
78 V->reshape(CIRCUIT->nv);
79 V->bind_all(CIRCUIT->logv, CH->g, F);
80 claimV = V->scalar();
81 }
82
83 // Consider claimV on the binding to P.G as two (identical)
84 // claims, so we can get the induction going. Thus, alpha in
85 // the first layer is redundant.
86 *cl = claims{
87 .nv = CIRCUIT->nv,
88 .logv = CIRCUIT->logv,
89 .claim = {claimV, claimV},
90 .q = CH->q,
91 .g = {CH->g, CH->g},
92 };
93
94 return layers(why, cl, CIRCUIT, PROOF, ts, CH, F);
95 }
96
97 VerifierLayers() = delete;
98
99 private:
100 struct desire {
101 bool cond;
102 const char* why;
103 };
104
105 static bool check(const char** why, size_t n, const desire* d) {
106 for (size_t i = 0; i < n; ++i) {
107 if (!d[i].cond) {
108 *why = d[i].why;
109 return false;
110 }
111 }
112 return true;
113 }
114
115 // Verify CLAIM for one layer and update CLAIM in-place as next
116 // claim. Return TRUE on success, and (FALSE, why) on failure.
117 static bool layer_c(const char** why, Elt* claim, size_t logc,
118 const LayerProof<Field>* plr, LayerChallenge<Field>* ch,
119 TranscriptSumcheck<Field>& ts, const Field& F) {
120 for (size_t round = 0; round < logc; ++round) {
121 // Change verification equation from
122 // claim =? (p(0) + p(1))
123 // to p(1) = claim - p(0).
124 auto tp = plr->cp[round];
125 auto t1 = F.subf(*claim, tp.t_[0]);
126 ch->cb[round] = ts.round(plr->cp[round]);
127 auto p = tp.to_poly(t1);
128 *claim = p.eval_lagrange(ch->cb[round], F);
129 }
130
131 return true;
132 }
133
134 static bool layer_h(const char** why, Elt* claim, size_t logw,
135 const LayerProof<Field>* plr, LayerChallenge<Field>* ch,
136 TranscriptSumcheck<Field>& ts, const Field& F) {
137 for (size_t round = 0; round < logw; ++round) {
138 for (size_t hand = 0; hand < 2; ++hand) {
139 // Change verification equation from
140 // claim =? (p(0) + p(1))
141 // to p(1) = claim - p(0).
142 auto tp = plr->hp[hand][round];
143 auto t1 = F.subf(*claim, tp.t_[0]);
144 ch->hb[hand][round] = ts.round(tp);
145 auto p = tp.to_poly(t1);
146 *claim = p.eval_lagrange(ch->hb[hand][round], F);
147 }
148 }
149 return true;
150 }
151
152 // Verify CLAIMS for all layers and update CLAIMS in-place. Return
153 // TRUE on success, and (FALSE, why) on failure.
154 static bool layers(const char** why, claims* cl,
155 const Circuit<Field>* CIRCUIT, const Proof<Field>* PROOF,
156 TranscriptSumcheck<Field>& ts, Challenge<Field>* CH,
157 const Field& F) {
158 for (size_t ly = 0; ly < CIRCUIT->nl; ++ly) {
159 auto clr = &CIRCUIT->l.at(ly);
160 auto plr = &PROOF->l[ly];
161 auto challenge = &CH->l[ly];
162
163 // the claim is then an affine combination of the two
164 // inductive claims
165 ts.begin_layer(challenge->alpha, challenge->beta, ly);
166 Elt claim = F.addf(cl->claim[0], F.mulf(challenge->alpha, cl->claim[1]));
167
168 if (!layer_c(why, &claim, CIRCUIT->logc, plr, challenge, ts, F)) {
169 return false;
170 }
171
172 if (!layer_h(why, &claim, clr->logw, plr, challenge, ts, F)) {
173 return false;
174 }
175
176 // Now verify CLAIM = EQ[Q,C] QUAD[R,L] W[R,C] W[L,C]
177 // where W[R,C], W[L,C] are in the proof.
178
179 // bind QUAD[g|r,l] to the alpha-combination of the
180 // two G values GR, GL
181 auto QUAD = clr->quad->clone();
182 QUAD->bind_g(cl->logv, cl->g[0], cl->g[1], challenge->alpha,
183 challenge->beta, F);
184
185 // bind QUAD[G|r,l] to R, L
186 for (size_t round = 0; round < clr->logw; ++round) {
187 for (size_t hand = 0; hand < 2; ++hand) {
188 QUAD->bind_h(challenge->hb[hand][round], hand, F);
189 }
190 }
191
192 // got = EQ[Q,C] QUAD[G|R,L] W[R,C] W[L,C], where
193 // W[.,C] is in the proof.
194 Elt got =
195 Eq<Field>::eval(CIRCUIT->logc, CIRCUIT->nc, cl->q, challenge->cb, F);
196 F.mul(got, QUAD->scalar());
197 F.mul(got, plr->wc[0]);
198 F.mul(got, plr->wc[1]);
199
200 if (got != claim) {
201 *why = "got != claim (layer)";
202 return false;
203 }
204
205 // Add wc[0,1] to transcript
206 ts.write(&plr->wc[0], 1, 2);
207
208 // Reduce to two claims on W[R,C] and W[L,C]
209 *cl = claims{
210 .nv = clr->nw,
211 .logv = clr->logw,
212 .claim = {plr->wc[0], plr->wc[1]},
213 .q = challenge->cb,
214 .g = {challenge->hb[0], challenge->hb[1]},
215 };
216 }
217 return true;
218 }
219};
220} // namespace proofs
221
222#endif // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_
Definition dense.h:37
Definition transcript_sumcheck.h:32
Definition circuit.h:117
Definition circuit.h:45
Definition gf2_128.h:63
Definition circuit.h:132
Definition verifier_layers.h:40