circom/src/r1csfile.js
src/r1csfile.jsRaw
1
2const fastFile = require("fastfile");
3const assert = require("assert");
4const BigArray = require("./bigarray");
5
6module.exports.buildR1cs = buildR1cs;
7
8
9async function buildR1cs(ctx, fileName) {
10
11 const fd = await fastFile.createOverride(fileName, 1<<24, 1<<22);
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], 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 BigArray(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.writeULE64(headerSize, pHeaderSize);
92 await fd.writeULE64(constraintsSize, pConstraintsSize);
93 await fd.writeULE64(mapSize, pMapSize);
94
95 await fd.close();
96
97 function writeConstraint(c, ctIdx) {
98 const n8 = ctx.F.n8;
99 const idxA = Object.keys(c.a.coefs);
100 const idxB = Object.keys(c.b.coefs);
101 const idxC = Object.keys(c.c.coefs);
102 const buff = new Uint8Array((idxA.length+idxB.length+idxC.length)*(n8+4) + 12);
103 const buffV = new DataView(buff.buffer);
104 let o=0;
105
106 buffV.setUint32(o, idxA.length, true); o+=4;
107 for (let i=0; i<idxA.length; i++) {
108 const coef = idxA[i];
109 let lSignal = ctx.signals[coef];
110 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
111 if (lSignal.id >= NWires) throw new Error("Signal Coef A: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
112 buffV.setUint32(o, lSignal.id, true); o+=4;
113 ctx.F.toRprLE(buff, o, c.a.coefs[coef]); o+=n8;
114 }
115
116 buffV.setUint32(o, idxB.length, true); o+=4;
117 for (let i=0; i<idxB.length; i++) {
118 const coef = idxB[i];
119 let lSignal = ctx.signals[coef];
120 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
121 if (lSignal.id >= NWires) throw new Error("Signal Coef B: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
122 buffV.setUint32(o, lSignal.id, true); o+=4;
123 ctx.F.toRprLE(buff, o, c.b.coefs[coef]); o+=n8;
124 }
125
126 buffV.setUint32(o, idxC.length, true); o+=4;
127 for (let i=0; i<idxC.length; i++) {
128 const coef = idxC[i];
129 let lSignal = ctx.signals[coef];
130 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
131 if (lSignal.id >= NWires) throw new Error("Signal Coef C: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
132 buffV.setUint32(o, lSignal.id, true); o+=4;
133 ctx.F.toRprLE(buff, o, ctx.F.neg(c.c.coefs[coef])); o+=n8;
134 }
135
136 return fd.write(buff);
137 }
138
139 async function writeBigInt(n, pos) {
140
141 ctx.F.toRprLE(buffBigInt, 0, n);
142
143 await fd.write(buffBigInt, pos);
144
145 }
146}