circom/src/r1csfile.js
src/r1csfile.jsRaw
1
2const fastFile = require("fastfile");
3const assert = require("assert");
4
5module.exports.buildR1cs = buildR1cs;
6
7
8async function buildR1cs(ctx, fileName) {
9
10 const fd = await fastFile.createOverride(fileName);
11
12
13 const buffBigInt = new Uint8Array(ctx.F.n8);
14
15 const type = "r1cs";
16 const buff = new Uint8Array(4);
17 for (let i=0; i<4; i++) buff[i] = type.charCodeAt(i);
18 await fd.write(buff, 0); // Magic "r1cs"
19
20 await fd.writeULE32(1); // Version
21 await fd.writeULE32(3); // Number of Sections
22
23 // Write the header
24 ///////////
25 await fd.writeULE32(1); // Header type
26 const pHeaderSize = fd.pos;
27 await fd.writeULE64(0); // Temporally set to 0 length
28
29
30 const n8 = (Math.floor( (ctx.F.bitLength - 1) / 64) +1)*8;
31 // Field Def
32 await fd.writeULE32(n8); // Temporally set to 0 length
33 await writeBigInt(ctx.F.p);
34
35 const NWires =
36 ctx.totals[ctx.stONE] +
37 ctx.totals[ctx.stOUTPUT] +
38 ctx.totals[ctx.stPUBINPUT] +
39 ctx.totals[ctx.stPRVINPUT] +
40 ctx.totals[ctx.stINTERNAL];
41
42 await fd.writeULE32(NWires);
43 await fd.writeULE32(ctx.totals[ctx.stOUTPUT]);
44 await fd.writeULE32(ctx.totals[ctx.stPUBINPUT]);
45 await fd.writeULE32(ctx.totals[ctx.stPRVINPUT]);
46 await fd.writeULE64(ctx.signals.length);
47 await fd.writeULE32(ctx.constraints.length);
48
49 const headerSize = fd.pos - pHeaderSize - 8;
50
51 // Write constraints
52 ///////////
53 await fd.writeULE32(2); // Constraints type
54 const pConstraintsSize = fd.pos;
55 await fd.writeULE64(0); // Temporally set to 0 length
56
57 for (let i=0; i<ctx.constraints.length; i++) {
58 if ((ctx.verbose)&&(i%10000 == 0)) {
59 if (ctx.verbose) console.log("writing constraint: ", i);
60 }
61 await writeConstraint(ctx.constraints[i]);
62 }
63
64 const constraintsSize = fd.pos - pConstraintsSize - 8;
65
66 // Write map
67 ///////////
68 await fd.writeULE32(3); // wires2label type
69 const pMapSize = fd.pos;
70 await fd.writeULE64(0); // Temporally set to 0 length
71
72
73 const arr = new Array(NWires);
74 for (let i=0; i<ctx.signals.length; i++) {
75 const outIdx = ctx.signals[i].id;
76 if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
77 assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
78 if (outIdx>=NWires) continue; // Is a constant or a discarded variable
79 if (typeof arr[ctx.signals[i].id] == "undefined") {
80 arr[outIdx] = i;
81 }
82 }
83 for (let i=0; i<arr.length; i++) {
84 await fd.writeULE64(arr[i]);
85 if ((ctx.verbose)&&(i%100000 == 0)) console.log(`writing wire2label map: ${i}/${arr.length}`);
86 }
87
88 const mapSize = fd.pos - pMapSize - 8;
89
90 // Write sizes
91 await fd.writeULE32(headerSize, pHeaderSize);
92 await fd.writeULE32(constraintsSize, pConstraintsSize);
93 await fd.writeULE32(mapSize, pMapSize);
94
95 await fd.close();
96
97 async function writeConstraint(c) {
98 await writeLC(c.a);
99 await writeLC(c.b);
100 await writeLC(ctx.lc.neg(c.c));
101 }
102
103 async function writeLC(lc) {
104 const idxs = Object.keys(lc.coefs);
105 await fd.writeULE32(idxs.length);
106 for (let s in lc.coefs) {
107 let lSignal = ctx.signals[s];
108
109 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
110
111 await fd.writeULE32(lSignal.id);
112 await writeBigInt(lc.coefs[s]);
113 }
114 }
115
116 async function writeBigInt(n, pos) {
117
118 ctx.F.toRprLE(buffBigInt, 0, n);
119
120 await fd.write(buffBigInt, pos);
121
122 }
123}