circom/src/r1csfile.js
src/r1csfile.jsRaw
1
2const fs = require("fs");
3const assert = require("assert");
4const lc = require("./lcalgebra");
5const bigInt = require("big-integer");
6
7module.exports.buildR1cs = buildR1cs;
8module.exports.loadR1cs = loadR1cs;
9
10async function loadR1cs(fileName, loadConstraints, loadMap) {
11 const res = {};
12 const fd = await fs.promises.open(fileName, "r");
13
14 const b = Buffer.allocUnsafe(4);
15 await fd.read(b, 0, 4, 0);
16
17 if (b.toString() != "r1cs") assert(false, "Invalid File format");
18
19 let p=4;
20
21 let v = await readU32();
22
23 if (v>1) assert(false, "Version not supported");
24
25 const nSections = await readU32();
26
27 let pHeader;
28 let pConstraints;
29 let headerSize;
30 let constraintsSize;
31 let pMap;
32 let mapSize;
33 for (let i=0; i<nSections; i++) {
34 let ht = await readU32();
35 let hl = await readU32();
36 if (ht == 1) {
37 if (typeof pHeader != "undefined") assert(false, "File has two headder sections");
38 pHeader = p;
39 headerSize = hl;
40 } else if (ht==2) {
41 if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections");
42 pConstraints = p;
43 constraintsSize = hl;
44 } else if (ht==3) {
45 pMap = p;
46 mapSize = hl;
47 }
48 p += hl;
49 }
50
51 if (typeof pHeader == "undefined") assert(false, "File has two header");
52
53 // Read Header
54 p = pHeader;
55 const fieldDefSize = await readU32();
56 const pFieldDef = p;
57
58 const defType = await readU32();
59 if (defType != 1) if (typeof pConstraints != "undefined") assert(false, "Field type not supported");
60
61 res.prime = await readBigInt();
62
63 if ( p != pFieldDef + fieldDefSize) assert("Invalid fieldDef size");
64
65 const bigIntFormat = await readU32();
66 if (bigIntFormat != 0) assert(false, "BigInt format not supported");
67
68 const idSize = await readU32();
69 if (idSize != 4) assert(false, "idSize not supported. Mus be 4");
70
71 res.nWires = await readU32();
72 res.nPubOuts = await readU32();
73 res.nPubIns = await readU32();
74 res.nPrvIns = await readU32();
75 res.nLabels = await readU32();
76 res.nConstraints = await readU32();
77
78 if (p != pHeader + headerSize) assert(false, "Invalid header section size");
79
80 if (loadConstraints) {
81 // Read Constraints
82 p = pConstraints;
83
84 res.constraints = [];
85 for (let i=0; i<res.nConstraints; i++) {
86 const c = await readConstraint();
87 res.constraints.push(c);
88 }
89 if (p != pConstraints + constraintsSize) assert(false, "Invalid constraints size");
90 }
91
92 // Read Labels
93
94 if (loadMap) {
95 p = pMap;
96
97 res.map = [];
98 for (let i=0; i<res.nLabels; i++) {
99 const idx = await readU32();
100 res.map.push(idx);
101 }
102 if (p != pMap + mapSize) assert(false, "Invalid Map size");
103 }
104
105 await fd.close();
106
107 return res;
108
109 async function readU32() {
110 const b = Buffer.allocUnsafe(4);
111 await fd.read(b, 0, 4, p);
112
113 p+=4;
114
115 return b.readInt32LE(0);
116 }
117
118 async function readBigInt() {
119 const bl = Buffer.allocUnsafe(1);
120 await fd.read(bl, 0, 1, p);
121 p++;
122
123 const l = bl[0];
124 const b = Buffer.allocUnsafe(l);
125 await fd.read(b, 0, l, p);
126 p += l;
127
128 const arr = Uint8Array.from(b);
129
130 const arrr = new Array(arr.length);
131 for (let i=0; i<arr.length; i++) {
132 arrr[i] = arr[arr.length-1-i];
133 }
134
135 const n = bigInt.fromArray(arrr, 256);
136
137 return n;
138 }
139
140 async function readConstraint() {
141 const c = {};
142 c.a = await readLC();
143 c.b = await readLC();
144 c.c = await readLC();
145 return c;
146 }
147
148 async function readLC() {
149 const lc= {};
150 const nIdx = await readU32();
151 for (let i=0; i<nIdx; i++) {
152 const idx = await readU32();
153 const val = await readBigInt();
154 lc[idx] = val;
155 }
156 return lc;
157 }
158}
159
160async function buildR1cs(ctx, fileName) {
161
162 const fd = await fs.promises.open(fileName, "w");
163
164
165 await fd.write("r1cs"); // Magic "r1cs"
166
167 let p = 4;
168 await writeU32(1); // Version
169 await writeU32(3); // Number of Sections
170
171 // Write the header
172 ///////////
173 await writeU32(1); // Header type
174 const pHeaderSize = p;
175 await writeU32(0); // Temporally set to 0 length
176
177
178 // Field Def
179 const pFieldDefSize = p;
180 await writeU32(0); // Temporally set to 0 length
181 await writeU32(1);
182 await writeBigInt(ctx.field.p);
183 const fieldDefSize = p - pFieldDefSize - 4;
184
185 await writeU32(0); // Variable bigInt format
186 await writeU32(4); // Id Size
187
188 const NWires =
189 ctx.totals[ctx.stONE] +
190 ctx.totals[ctx.stOUTPUT] +
191 ctx.totals[ctx.stPUBINPUT] +
192 ctx.totals[ctx.stPRVINPUT] +
193 ctx.totals[ctx.stINTERNAL];
194
195 await writeU32(NWires);
196 await writeU32(ctx.totals[ctx.stOUTPUT]);
197 await writeU32(ctx.totals[ctx.stPUBINPUT]);
198 await writeU32(ctx.totals[ctx.stPRVINPUT]);
199 await writeU32(ctx.signals.length);
200 await writeU32(ctx.constraints.length);
201
202 const headerSize = p - pHeaderSize - 4;
203
204 // Write constraints
205 ///////////
206 await writeU32(2); // Constraints type
207 const pConstraintsSize = p;
208 await writeU32(0); // Temporally set to 0 length
209
210 for (let i=0; i<ctx.constraints.length; i++) {
211 if ((ctx.verbose)&&(i%10000 == 0)) {
212 if (ctx.verbose) console.log("writing constraint: ", i);
213 await fd.datasync();
214 }
215 await writeConstraint(ctx.constraints[i]);
216 }
217
218 const constraintsSize = p - pConstraintsSize - 4;
219
220 // Write map
221 ///////////
222 await writeU32(3); // wires2label type
223 const pMapSize = p;
224 await writeU32(0); // Temporally set to 0 length
225
226
227 const arr = new Array(NWires);
228 for (let i=0; i<ctx.signals.length; i++) {
229 const outIdx = ctx.signals[i].id;
230 if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
231 assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
232 if (outIdx>=NWires) continue; // Is a constant or a discarded variable
233 if (typeof arr[ctx.signals[i].id] == "undefined") {
234 arr[outIdx] = i;
235 }
236 }
237 for (let i=0; i<arr.length; i++) {
238 await writeU32(arr[i]);
239 if ((ctx.verbose)&&(i%100000)) console.log("writing label2wire map: ", i);
240 }
241
242 const mapSize = p - pMapSize -4;
243
244 // Write sizes
245 await writeU32(headerSize, pHeaderSize);
246 await writeU32(fieldDefSize, pFieldDefSize);
247 await writeU32(constraintsSize, pConstraintsSize);
248 await writeU32(mapSize, pMapSize);
249
250 await fd.sync();
251 await fd.close();
252
253 async function writeU32(v, pos) {
254 const b = Buffer.allocUnsafe(4);
255 b.writeInt32LE(v);
256
257 await fd.write(b, 0, 4, pos);
258
259 if (typeof(pos) == "undefined") p += 4;
260 }
261
262 async function writeConstraint(c) {
263 await writeLC(c.a);
264 await writeLC(c.b);
265 await writeLC(lc.negate(c.c));
266 }
267
268 async function writeLC(lc) {
269 const idxs = Object.keys(lc.values);
270 await writeU32(idxs.length);
271 for (let s in lc.values) {
272 let lSignal = ctx.signals[s];
273
274 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
275
276 await writeU32(lSignal.id);
277 await writeBigInt(lc.values[s]);
278 }
279 }
280
281 async function writeBigInt(n) {
282
283 const bytes = bigInt(n).toArray(256).value.reverse();
284
285 await fd.write(Buffer.from([bytes.length, ...bytes ]));
286
287 p += bytes.length+1;
288 }
289}