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 await fd.write(Buffer.from("r1cs"), 0); // Magic "r1cs"
13
14 let p = 4;
15 await writeU32(1); // Version
16 await writeU32(3); // Number of Sections
17
18 // Write the header
19 ///////////
20 await writeU32(1); // Header type
21 const pHeaderSize = p;
22 await writeU64(0); // Temporally set to 0 length
23
24
25 const n8 = (Math.floor( (ctx.F.bitLength - 1) / 64) +1)*8;
26 // Field Def
27 await writeU32(n8); // Temporally set to 0 length
28 await writeBigInt(ctx.F.p);
29
30 const NWires =
31 ctx.totals[ctx.stONE] +
32 ctx.totals[ctx.stOUTPUT] +
33 ctx.totals[ctx.stPUBINPUT] +
34 ctx.totals[ctx.stPRVINPUT] +
35 ctx.totals[ctx.stINTERNAL];
36
37 await writeU32(NWires);
38 await writeU32(ctx.totals[ctx.stOUTPUT]);
39 await writeU32(ctx.totals[ctx.stPUBINPUT]);
40 await writeU32(ctx.totals[ctx.stPRVINPUT]);
41 await writeU64(ctx.signals.length);
42 await writeU32(ctx.constraints.length);
43
44 const headerSize = p - pHeaderSize - 8;
45
46 // Write constraints
47 ///////////
48 await writeU32(2); // Constraints type
49 const pConstraintsSize = p;
50 await writeU64(0); // Temporally set to 0 length
51
52 for (let i=0; i<ctx.constraints.length; i++) {
53 if ((ctx.verbose)&&(i%10000 == 0)) {
54 if (ctx.verbose) console.log("writing constraint: ", i);
55 }
56 await writeConstraint(ctx.constraints[i]);
57 }
58
59 const constraintsSize = p - pConstraintsSize - 8;
60
61 // Write map
62 ///////////
63 await writeU32(3); // wires2label type
64 const pMapSize = p;
65 await writeU64(0); // Temporally set to 0 length
66
67
68 const arr = new Array(NWires);
69 for (let i=0; i<ctx.signals.length; i++) {
70 const outIdx = ctx.signals[i].id;
71 if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
72 assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
73 if (outIdx>=NWires) continue; // Is a constant or a discarded variable
74 if (typeof arr[ctx.signals[i].id] == "undefined") {
75 arr[outIdx] = i;
76 }
77 }
78 for (let i=0; i<arr.length; i++) {
79 await writeU64(arr[i]);
80 if ((ctx.verbose)&&(i%100000)) console.log("writing wire2label map: ", i);
81 }
82
83 const mapSize = p - pMapSize - 8;
84
85 // Write sizes
86 await writeU32(headerSize, pHeaderSize);
87 await writeU32(constraintsSize, pConstraintsSize);
88 await writeU32(mapSize, pMapSize);
89
90 await fd.close();
91
92 async function writeU32(v, pos) {
93 let o = (typeof pos == "undefined") ? p : pos;
94
95 const b = Buffer.allocUnsafe(4);
96 b.writeInt32LE(v);
97
98 await fd.write(b, o);
99
100 if (typeof(pos) == "undefined") p += 4;
101 }
102
103 async function writeU64(v, pos) {
104 let o = (typeof pos == "undefined") ? p : pos;
105
106 const b = Buffer.allocUnsafe(8);
107
108 const LSB = v & 0xFFFFFFFF;
109 const MSB = Math.floor(v / 0x100000000);
110 b.writeInt32LE(LSB, 0);
111 b.writeInt32LE(MSB, 4);
112
113 await fd.write(b, o);
114
115 if (typeof(pos) == "undefined") p += 8;
116 }
117
118 async function writeConstraint(c) {
119 await writeLC(c.a);
120 await writeLC(c.b);
121 await writeLC(ctx.lc.neg(c.c));
122 }
123
124 async function writeLC(lc) {
125 const idxs = Object.keys(lc.coefs);
126 await writeU32(idxs.length);
127 for (let s in lc.coefs) {
128 let lSignal = ctx.signals[s];
129
130 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
131
132 await writeU32(lSignal.id);
133 await writeBigInt(lc.coefs[s]);
134 }
135 }
136
137 async function writeBigInt(n, pos) {
138
139 let o = (typeof pos == "undefined") ? p : pos;
140
141 const s = n.toString(16);
142 const b = Buffer.from(s.padStart(n8*2, "0"), "hex");
143 const buff = Buffer.allocUnsafe(b.length);
144 for (let i=0; i<b.length; i++) buff[i] = b[b.length-1-i];
145
146 await fd.write(buff, o);
147
148 if (typeof(pos) == "undefined") p += n8;
149 }
150}