Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
zk_prover.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_ZK_ZK_PROVER_H_
16#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROVER_H_
17
18#include <stddef.h>
19
20#include <memory>
21#include <vector>
22
23#include "arrays/dense.h"
24#include "ligero/ligero_param.h"
25#include "ligero/ligero_prover.h"
26#include "random/random.h"
27#include "random/transcript.h"
28#include "sumcheck/circuit.h"
29#include "sumcheck/prover_layers.h"
30#include "sumcheck/transcript_sumcheck.h"
31#include "util/log.h"
32#include "util/panic.h"
33#include "zk/zk_common.h"
34#include "zk/zk_proof.h"
35
36namespace proofs {
37// ZK Prover
38//
39// This class implements a zero-knowledge argument over a sumcheck transcript
40// by first committing to a sumcheck witness and a random pad to encrypt
41// a sumcheck transcript, then running the sumcheck protocol over the original
42// claim and witness, but outputting the encrypted transcript, and finally
43// using a Ligero prover to prove the statement: "the committed witness and
44// pad, when used to decrypt the encrypted sumcheck transcript satisfies the
45// sumcheck verifier."
46//
47// While this statement is complex, it can be implemented easily because
48// the sumcheck verifier essentially checks the evaluations of degree-2 or -3
49// polynomials, and performs one multiplication per layer of the circuit. The
50// Hyrax paper makes a similar observation, but uses an elliptic-curve based
51// proof, whereas here we use the Ligero system.
52template <class Field, class ReedSolomonFactory>
53class ZkProver : public ProverLayers<Field> {
54 using super = ProverLayers<Field>;
55 using typename super::bindings;
56 using Elt = typename Field::Elt;
57 using typename super::inputs;
58
59 public:
60 ZkProver(const Circuit<Field>& CIRCUIT, const Field& F,
61 const ReedSolomonFactory& rs_factory)
62 : ProverLayers<Field>(F),
63 c_(CIRCUIT),
64 n_witness_(c_.ninputs - c_.npub_in),
65 f_(F),
66 rsf_(rs_factory),
67 pad_(c_.nl),
68 witness_(n_witness_),
69 lqc_(c_.nl),
70 lp_(nullptr) {}
71
72 void commit(ZkProof<Field>& zkp, const Dense<Field>& W, Transcript& tp,
73 RandomEngine& rng) {
74 log(INFO, "ZK Commit start");
75
76 // Copy witnesses for commitment
77 // Layout of the com: 0 ...<witnesses>... start_pad <pad> len
78 // Only commit the private witnesses, which begin at index c_.npub_in.
79 for (size_t i = 0; i < n_witness_; ++i) {
80 witness_[i] = W.v_[i + c_.npub_in];
81 }
82
83 // Rebase the circuit SUBFIELD_BOUNDARY (if any) to start at
84 // NPUB_IN,
85 size_t subfield_boundary = 0;
86 if (c_.subfield_boundary >= c_.npub_in) {
87 subfield_boundary = c_.subfield_boundary - c_.npub_in;
88 }
89
90 // Fill pad with random values, add pad to witness, record lqc.
91 fill_pad(rng);
92 ZkCommon<Field>::setup_lqc(c_, lqc_, n_witness_ /* = start_pad */);
93
94 // Commit to witness and pad.
95 lp_ = std::make_unique<LigeroProver<Field, ReedSolomonFactory>>(zkp.param);
96 lp_->commit(zkp.com, tp, &witness_[0], subfield_boundary, &lqc_[0], rsf_,
97 rng, f_);
98
99 log(INFO, "ZK Commitment done");
100 }
101
102 bool prove(ZkProof<Field>& zkp, const Dense<Field>& W, Transcript& tsp) {
103 check(lp_ != nullptr, "must run commit before prove");
104
105 // Interpret W as public parameters, we only append
106 // c_.npub_in elements of W to the transcript
107 ZkCommon<Field>::initialize_sumcheck_fiat_shamir(tsp, c_, W, f_);
108 Transcript tst = tsp.clone();
109
110 // Run sumcheck to generate a padded proof.
111 inputs in;
112 auto V = super::eval_circuit(&in, &c_, W.clone(), f_);
113 if (V == nullptr) {
114 log(ERROR, "eval_circuit failed");
115 return false;
116 }
117 for (size_t i = 0; i < V->n1_; ++i) {
118 if (V->v_[i] != f_.zero()) {
119 log(ERROR, "V->v_[i] != F.zero()");
120 return false;
121 };
122 }
123 bindings bnd;
124 ProofAux<Field> aux(c_.nl);
125
126 TranscriptSumcheck<Field> tsts(tst, f_);
127 super::prove(&zkp.proof, &pad_, &c_, in, &aux, bnd, tsts, f_);
128 log(INFO, "ZK sumcheck done");
129
130 // 5. Simulate the verifier to assemble constraints on the committed vals.
131 // Form the sparse matrix A and vector b such that A*w = b.
132 std::vector<LigeroLinearConstraint<Field>> a;
133 std::vector<Elt> b;
134 size_t ci = ZkCommon<Field>::verifier_constraints(c_, W, zkp.proof, &aux, a,
135 b, tsp, n_witness_, f_);
136 log(INFO, "ZK constraints done");
137
138 // 6. Produce proof over commitment.
139 // For FS soundness, it is ok for hash_of_A to be any string.
140 // In the interactive version, the verifier provides a challenge for the
141 // com proof. The last prover message is the (wc_l,wc_r) pair, and this
142 // has already been added to the transcript.
143 const LigeroHash hash_of_A{0xde, 0xad, 0xbe, 0xef};
144 lp_->prove(zkp.com_proof, tsp, ci, a.size(), &a[0], hash_of_A, &lqc_[0],
145 rsf_, f_);
146
147 log(INFO, "Prover Done: flag");
148 return true;
149 }
150
151 // Fill proof with random pad values for a given circuit.
152 void fill_pad(RandomEngine& rng) {
153 for (size_t i = 0; i < c_.nl; ++i) {
154 for (size_t j = 0; j < c_.logc; ++j) {
155 for (size_t k = 0; k < 4; ++k) {
156 if (k != 1) { // P(1) optimization
157 Elt r = rng.elt(f_);
158 pad_.l[i].cp[j].t_[k] = r;
159 witness_.push_back(r);
160 } else {
161 pad_.l[i].cp[j].t_[k] = f_.zero();
162 }
163 }
164 }
165 for (size_t j = 0; j < c_.l[i].logw; ++j) {
166 for (size_t h = 0; h < 2; ++h) {
167 for (size_t k = 0; k < 3; ++k) {
168 if (k != 1) { // P(1) optimization
169 Elt r = rng.elt(f_);
170 pad_.l[i].hp[h][j].t_[k] = r;
171 witness_.push_back(r);
172 } else {
173 pad_.l[i].hp[h][j].t_[k] = f_.zero();
174 }
175 }
176 }
177 }
178 for (size_t k = 0; k < 2; ++k) {
179 Elt r = rng.elt(f_);
180 pad_.l[i].wc[k] = r;
181 witness_.push_back(r);
182 }
183
184 // Commit to product of pads for product proof.
185 Elt rr = f_.mulf(pad_.l[i].wc[0], pad_.l[i].wc[1]);
186 witness_.push_back(rr);
187 }
188 }
189
190 const Circuit<Field>& c_;
191 const size_t n_witness_;
192 const Field& f_;
193 const ReedSolomonFactory& rsf_;
194 Proof<Field> pad_;
195 std::vector<Elt> witness_;
196 std::vector<LigeroQuadraticConstraint> lqc_;
197 std::unique_ptr<LigeroProver<Field, ReedSolomonFactory>> lp_;
198};
199
200} // namespace proofs
201
202#endif // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROVER_H_
Definition dense.h:37
Definition random.h:32
Definition reed_solomon.h:133
Definition transcript_sumcheck.h:32
Definition transcript.h:65
Definition circuit.h:45
Definition gf2_128.h:63
Definition ligero_param.h:426
Definition circuit.h:149
Definition circuit.h:130
Definition prover_layers.h:104
Definition zk_proof.h:47