circom/src/r1csfile.js
src/r1csfile.jsRaw
1
2const fs = require("fs");
3const assert = require("assert");
4const Scalar = require("ffjavascript").Scalar;
5
6module.exports.buildR1cs = buildR1cs;
7
8async function buildR1cs(ctx, fileName) {
9
10 const fd = await fs.promises.open(fileName, "w");
11
12
13 await fd.write("r1cs"); // Magic "r1cs"
14
15 let p = 4;
16 await writeU32(1); // Version
17 await writeU32(3); // Number of Sections
18
19 // Write the header
20 ///////////
21 await writeU32(1); // Header type
22 const pHeaderSize = p;
23 await writeU64(0); // Temporally set to 0 length
24
25
26 const n8 = (Math.floor( (ctx.F.bitLength - 1) / 64) +1)*8;
27 // Field Def
28 await writeU32(n8); // Temporally set to 0 length
29 await writeBigInt(ctx.F.p);
30
31 const NWires =
32 ctx.totals[ctx.stONE] +
33 ctx.totals[ctx.stOUTPUT] +
34 ctx.totals[ctx.stPUBINPUT] +
35 ctx.totals[ctx.stPRVINPUT] +
36 ctx.totals[ctx.stINTERNAL];
37
38 await writeU32(NWires);
39 await writeU32(ctx.totals[ctx.stOUTPUT]);
40 await writeU32(ctx.totals[ctx.stPUBINPUT]);
41 await writeU32(ctx.totals[ctx.stPRVINPUT]);
42 await writeU64(ctx.signals.length);
43 await writeU32(ctx.constraints.length);
44
45 const headerSize = p - pHeaderSize - 8;
46
47 // Write constraints
48 ///////////
49 await writeU32(2); // Constraints type
50 const pConstraintsSize = p;
51 await writeU64(0); // Temporally set to 0 length
52
53 for (let i=0; i<ctx.constraints.length; i++) {
54 if ((ctx.verbose)&&(i%10000 == 0)) {
55 if (ctx.verbose) console.log("writing constraint: ", i);
56 await fd.datasync();
57 }
58 await writeConstraint(ctx.constraints[i]);
59 }
60
61 const constraintsSize = p - pConstraintsSize - 8;
62
63 // Write map
64 ///////////
65 await writeU32(3); // wires2label type
66 const pMapSize = p;
67 await writeU64(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 writeU64(arr[i]);
82 if ((ctx.verbose)&&(i%100000)) console.log("writing wire2label map: ", i);
83 }
84
85 const mapSize = p - pMapSize - 8;
86
87 // Write sizes
88 await writeU32(headerSize, pHeaderSize);
89 await writeU32(constraintsSize, pConstraintsSize);
90 await writeU32(mapSize, pMapSize);
91
92 await fd.sync();
93 await fd.close();
94
95 async function writeU32(v, pos) {
96 const b = Buffer.allocUnsafe(4);
97 b.writeInt32LE(v);
98
99 await fd.write(b, 0, 4, pos);
100
101 if (typeof(pos) == "undefined") p += 4;
102 }
103
104 async function writeU64(v, pos) {
105 const b = Buffer.allocUnsafe(8);
106
107 const LSB = v & 0xFFFFFFFF;
108 const MSB = Math.floor(v / 0x100000000);
109 b.writeInt32LE(LSB, 0);
110 b.writeInt32LE(MSB, 4);
111
112 await fd.write(b, 0, 8, pos);
113
114 if (typeof(pos) == "undefined") p += 8;
115 }
116
117 async function writeConstraint(c) {
118 await writeLC(c.a);
119 await writeLC(c.b);
120 await writeLC(ctx.lc.neg(c.c));
121 }
122
123 async function writeLC(lc) {
124 const idxs = Object.keys(lc.coefs);
125 await writeU32(idxs.length);
126 for (let s in lc.coefs) {
127 let lSignal = ctx.signals[s];
128
129 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
130
131 await writeU32(lSignal.id);
132 await writeBigInt(lc.coefs[s]);
133 }
134 }
135
136 async function writeBigInt(n, pos) {
137 const b = Buffer.allocUnsafe(n8);
138
139 const dwords = Scalar.toArray(n, 0x100000000);
140
141 for (let i=0; i<dwords.length; i++) {
142 b.writeUInt32LE(dwords[dwords.length-1-i], i*4, 4 );
143 }
144 b.fill(0, dwords.length*4);
145
146 await fd.write(b, 0, fs, pos);
147
148 if (typeof(pos) == "undefined") p += n8;
149 }
150}