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 function writeConstraint(c) {
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 buffV.setUint32(o, lSignal.id, true); o+=4;
112 ctx.F.toRprLE(buff, o, c.a.coefs[coef]); o+=n8;
113 }
114
115 buffV.setUint32(o, idxB.length, true); o+=4;
116 for (let i=0; i<idxB.length; i++) {
117 const coef = idxB[i];
118 let lSignal = ctx.signals[coef];
119 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
120 buffV.setUint32(o, lSignal.id, true); o+=4;
121
122 ctx.F.toRprLE(buff, o, c.b.coefs[coef]); o+=n8;
123 }
124
125 buffV.setUint32(o, idxC.length, true); o+=4;
126 for (let i=0; i<idxC.length; i++) {
127 const coef = idxC[i];
128 let lSignal = ctx.signals[coef];
129 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
130 buffV.setUint32(o, lSignal.id, true); o+=4;
131 ctx.F.toRprLE(buff, o, ctx.F.neg(c.c.coefs[coef])); o+=n8;
132 }
133
134 return fd.write(buff);
135 }
136
137 async function writeBigInt(n, pos) {
138
139 ctx.F.toRprLE(buffBigInt, 0, n);
140
141 await fd.write(buffBigInt, pos);
142
143 }
144}