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