Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
cbor_witness.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_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include <array>
22
23#include "circuits/cbor_parser/cbor_constants.h"
24#include "circuits/cbor_parser/cbor_pluck.h"
25#include "util/panic.h"
26
27namespace proofs {
28template <class Field>
29class CborWitness {
30 public:
31 using Elt = typename Field::Elt;
32 static constexpr size_t kNCounters = CborConstants::kNCounters;
33 static constexpr size_t kIndexBits = CborConstants::kIndexBits;
34 using counters = std::array<size_t, kNCounters>;
35 using vindex = std::array<Elt, kIndexBits>;
36
38 Elt encoded_sel_header;
39
40 // SLEN output value, used for debugging but not fed to the circuit
41 size_t slen_next_debug;
42 // counter values, used for debugging but not fed to the circuit
43 counters cc_debug;
44 size_t isel_debug;
45 };
46
48 Elt invprod_decode;
49 Elt cc0; // initial values of counter[0]
50 Elt invprod_parse;
51 };
52
53 using v8 = std::array<Elt, 8>;
54
55 explicit CborWitness(const Field& F) : f_(F) {}
56
57 // Return an index as an array of Elt, which can be stored into W[]
58 vindex index(size_t j) const {
59 const Field& F = f_; // shorthand
60 vindex r;
61 for (size_t i = 0; i < kIndexBits; ++i) {
62 r[i] = F.of_scalar((j >> i) & 1);
63 }
64 return r;
65 }
66
67 void fill_witnesses(size_t n, size_t input_len, const uint8_t bytes[/*n*/],
68 v8 in[/*n*/], position_witness pw[/*n*/],
69 global_witness& gw) const {
70 const Field& F = f_; // shorthand
71
72 // First pass to compute the number of top-level items. In the
73 // second pass, we will use this value to that all counters are 0
74 // at the end of the input.
75 size_t top_level_items;
76 {
77 // start with a value of cc[0] guaranteed not to
78 // underflow counter 0.
79 counters cc{{n + 1}};
80
81 size_t slen = 1;
82 for (size_t i = 0; i < n; ++i) {
83 bool overflow;
84 bool header = (slen == 1);
85 cc = counters_next(bytes[i], header,
86 /*have_nextb=*/(i + 1) < n,
87 /*nextb=*/(i + 1) < n ? bytes[i + 1] : 0, cc,
88 &overflow);
89 proofs::check(!overflow, "!overflow");
90 slen = next_slen(slen, n, bytes, i);
91 }
92
93 top_level_items = (n + 1) - cc[0];
94 }
95
96 // second pass starting with the correct counter values
97 {
98 counters cc{{top_level_items}};
99 Elt prod_parse = F.one();
100 Elt prod_decode = F.one();
101
102 size_t slen = 1;
103 for (size_t i = 0; i < n; ++i) {
104 bool overflow;
105 bool header = (slen == 1);
106
107 // Require all bytes to be 0 except the last N-INPUT_LEN.
108 // That is, the input must be aligned towards the end
109 // of arrays, and padded with zeroes at the beginning.
110 proofs::check(input_len <= n, "input_len <= n");
111 if (i + input_len < n) {
112 proofs::check(bytes[i] == 0, "bytes[i] == 0");
113 }
114
115 // set up input
116 for (size_t j = 0; j < 8; ++j) {
117 in[i][j] = F.of_scalar((bytes[i] >> j) & 1);
118 }
119
120 if (!header) {
121 F.mul(prod_decode, F.of_scalar(slen - 1));
122 }
123
124 // set up parse witness
125 size_t isel = kNCounters;
126 for (size_t l = kNCounters; l-- > 0;) {
127 if (cc[l] != 0) {
128 if (i > 0) {
129 F.mul(prod_parse, F.of_scalar(cc[l]));
130 }
131 isel = l;
132 break;
133 }
134 }
135
136 cc = counters_next(bytes[i], header,
137 /*have_nextb=*/(i + 1) < n,
138 /*nextb=*/(i + 1) < n ? bytes[i + 1] : 0, cc,
139 &overflow);
140 proofs::check(!overflow, "!overflow");
141 if (i == 0) {
142 gw.cc0 = F.of_scalar(cc[0]);
143 }
144 pw[i].cc_debug = cc;
145
146 // set up decode witness
147 size_t slen_next = next_slen(slen, n, bytes, i);
148 pw[i].slen_next_debug = slen_next;
149
150 // encode witnesses
151 pw[i].encoded_sel_header =
152 cbor_plucker_point<Field, kNCounters>()(header, isel, F);
153 pw[i].isel_debug = isel;
154
155 // advance slen
156 slen = slen_next;
157 }
158
159 gw.invprod_decode = F.invertf(prod_decode);
160 gw.invprod_parse = F.invertf(prod_parse);
161 }
162 }
163
164 private:
165 static size_t next_slen(size_t slen, size_t n, const uint8_t bytes[/*n*/],
166 size_t i) {
167 size_t slenm1 = slen - 1;
168 bool header = (slenm1 == 0);
169 if (header) {
170 if (i + 1 < n) {
171 return item_length(bytes[i], true, bytes[i + 1]);
172 } else {
173 return item_length(bytes[i], false, 0);
174 }
175 } else {
176 return slenm1;
177 }
178 }
179
180 // TODO [matteof 2023-11-03] Should not panic() here.
181 static size_t item_length(uint8_t b, bool valid_nextb, uint8_t nextb) {
182 size_t type = (b >> 5) & 0x7u;
183 size_t count = b & 0x1Fu;
184 bool count0_23 = (count < 24);
185 bool count24 = (count == 24);
186
187 switch (type) {
188 case 0: /* unsigned */
189 case 1: /* negative integer */
190 case 4: /* array */
191 case 5: /* map */
192 case 6: /* tag */
193 if (count0_23) {
194 return 1;
195 } else if (count24) {
196 return 2;
197 } else {
198 check(false, "unwitnessed count (atom)");
199 return 0;
200 }
201
202 case 2: /* bytes */
203 case 3: /* text */
204 if (count0_23) {
205 return 1 + count;
206 } else if (count24) {
207 if (valid_nextb) {
208 return 2 + nextb;
209 } else {
210 check(false, "invalid nextb");
211 return 0;
212 }
213 } else {
214 check(false, "unwitnessed count (bytes)");
215 return 0;
216 }
217
218 case 7: /* special */
219 check(false, "unwitnessed special");
220 return 0;
221
222 default:
223 check(false, "can't happen");
224 return 0;
225 }
226 }
227
228 static size_t decode_count(size_t count_in_header, bool have_nextb,
229 uint8_t nextb) {
230 if (count_in_header < 24) {
231 return count_in_header;
232 } else if (count_in_header == 24) {
233 if (have_nextb) {
234 return nextb;
235 } else {
236 check(false, "!have_nextb");
237 }
238 } else {
239 check(false, "count > 24");
240 }
241 return 0xdeadbeef;
242 }
243
244 static counters counters_next(uint8_t b, bool header, bool have_nextb,
245 uint8_t nextb, const counters& c,
246 bool* overflow) {
247 size_t type = (b >> 5) & 0x7u;
248 size_t count_in_header = b & 0x1Fu;
249 bool tagp = (type == 6);
250 bool arrayp = (type == 4);
251 bool mapp = (type == 5);
252
253 counters c1 = c;
254 *overflow = false;
255
256 for (size_t l = kNCounters; l-- > 0;) {
257 if (c[l] != 0) {
258 if (header) {
259 c1[l] = c[l] - 1;
260
261 if (tagp) {
262 if (l + 1 < kNCounters) {
263 c1[l + 1] = 1;
264 } else {
265 *overflow = true;
266 }
267 } else if (arrayp) {
268 if (l + 1 < kNCounters) {
269 c1[l + 1] = decode_count(count_in_header, have_nextb, nextb);
270 } else {
271 *overflow = true;
272 }
273 } else if (mapp) {
274 if (l + 1 < kNCounters) {
275 c1[l + 1] = 2 * decode_count(count_in_header, have_nextb, nextb);
276 } else {
277 *overflow = true;
278 }
279 }
280 }
281 break;
282 }
283 }
284
285 return c1;
286 }
287
288 private:
289 const Field& f_;
290};
291} // namespace proofs
292
293#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_
Definition cbor_witness.h:47
Definition cbor_witness.h:37
Definition gf2_128.h:63