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 // (p(0) + p(1))
122 Elt got = F.addf(plr->cp[round].t_[0], plr->cp[round].t_[1]);
123 if (got != *claim) {
124 *why = "got != claim (round_c)";
125 return false;
126 }
127 ch->cb[round] = ts.round(plr->cp[round]);
128 *claim = plr->cp[round].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 // (p(0) + p(1))
140 Elt got =
141 F.addf(plr->hp[hand][round].t_[0], plr->hp[hand][round].t_[1]);
142 if (got != *claim) {
143 *why = "got != claim (round_h)";
144 return false;
145 }
146 ch->hb[hand][round] = ts.round(plr->hp[hand][round]);
147 *claim = plr->hp[hand][round].eval_lagrange(ch->hb[hand][round], F);
148 }
149 }
150 return true;
151 }
152
153 // Verify CLAIMS for all layers and update CLAIMS in-place. Return
154 // TRUE on success, and (FALSE, why) on failure.
155 static bool layers(const char** why, claims* cl,
156 const Circuit<Field>* CIRCUIT, const Proof<Field>* PROOF,
157 TranscriptSumcheck<Field>& ts, Challenge<Field>* CH,
158 const Field& F) {
159 for (size_t ly = 0; ly < CIRCUIT->nl; ++ly) {
160 auto clr = &CIRCUIT->l.at(ly);
161 auto plr = &PROOF->l[ly];
162 auto challenge = &CH->l[ly];
163
164 // the claim is then an affine combination of the two
165 // inductive claims
166 ts.begin_layer(challenge->alpha, challenge->beta, ly);
167 Elt claim = F.addf(cl->claim[0], F.mulf(challenge->alpha, cl->claim[1]));
168
169 if (!layer_c(why, &claim, CIRCUIT->logc, plr, challenge, ts, F)) {
170 return false;
171 }
172
173 if (!layer_h(why, &claim, clr->logw, plr, challenge, ts, F)) {
174 return false;
175 }
176
177 // Now verify CLAIM = EQ[Q,C] QUAD[R,L] W[R,C] W[L,C]
178 // where W[R,C], W[L,C] are in the proof.
179
180 // bind QUAD[g|r,l] to the alpha-combination of the
181 // two G values GR, GL
182 auto QUAD = clr->quad->clone();
183 QUAD->bind_g(cl->logv, cl->g[0], cl->g[1], challenge->alpha,
184 challenge->beta, F);
185
186 // bind QUAD[G|r,l] to R, L
187 for (size_t round = 0; round < clr->logw; ++round) {
188 for (size_t hand = 0; hand < 2; ++hand) {
189 QUAD->bind_h(challenge->hb[hand][round], hand, F);
190 }
191 }
192
193 // got = EQ[Q,C] QUAD[G|R,L] W[R,C] W[L,C], where
194 // W[.,C] is in the proof.
195 Elt got =
196 Eq<Field>::eval(CIRCUIT->logc, CIRCUIT->nc, cl->q, challenge->cb, F);
197 F.mul(got, QUAD->scalar());
198 F.mul(got, plr->wc[0]);
199 F.mul(got, plr->wc[1]);
200
201 if (got != claim) {
202 *why = "got != claim (layer)";
203 return false;
204 }
205
206 // Add wc[0,1] to transcript
207 ts.write(&plr->wc[0], 1, 2);
208
209 // Reduce to two claims on W[R,C] and W[L,C]
210 *cl = claims{
211 .nv = clr->nw,
212 .logv = clr->logw,
213 .claim = {plr->wc[0], plr->wc[1]},
214 .q = challenge->cb,
215 .g = {challenge->hb[0], challenge->hb[1]},
216 };
217 }
218 return true;
219 }
220};
221} // namespace proofs
222
223#endif // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_
Definition dense.h:37
Definition transcript_sumcheck.h:32
Definition circuit.h:115
Definition circuit.h:45
Definition gf2_128.h:63
Definition circuit.h:130
Definition verifier_layers.h:40