circom/src/r1csfile.js
src/r1csfile.jsRaw
1
2const fs = require("fs");
3const assert = require("assert");
4const bigInt = require("big-integer");
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.field.p.bitLength() - 1) / 64) +1)*8;
27 // Field Def
28 await writeU32(n8); // Temporally set to 0 length
29 await writeBigInt(ctx.field.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 b.writeBigUInt64LE(BigInt(v));
107
108 await fd.write(b, 0, 8, pos);
109
110 if (typeof(pos) == "undefined") p += 8;
111 }
112
113 async function writeConstraint(c) {
114 await writeLC(c.a);
115 await writeLC(c.b);
116 await writeLC(ctx.lc.neg(c.c));
117 }
118
119 async function writeLC(lc) {
120 const idxs = Object.keys(lc.coefs);
121 await writeU32(idxs.length);
122 for (let s in lc.coefs) {
123 let lSignal = ctx.signals[s];
124
125 while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
126
127 await writeU32(lSignal.id);
128 await writeBigInt(lc.coefs[s]);
129 }
130 }
131
132 async function writeBigInt(n, pos) {
133 const b = Buffer.allocUnsafe(n8);
134
135 const dwords = bigInt(n).toArray(0x100000000).value;
136
137 for (let i=0; i<dwords.length; i++) {
138 b.writeUInt32LE(dwords[dwords.length-1-i], i*4, 4 );
139 }
140 b.fill(0, dwords.length*4);
141
142 await fd.write(b, 0, fs, pos);
143
144 if (typeof(pos) == "undefined") p += n8;
145 }
146}