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