arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/r1csfile.js
src/r1csfile.jsRaw
  1
  2const fs = require("fs");
  3const assert = require("assert");
  4const lc = require("./lcalgebra");
  5const bigInt = require("big-integer");
  6
  7module.exports.buildR1cs = buildR1cs;
  8module.exports.loadR1cs = loadR1cs;
  9
 10async function loadR1cs(fileName, loadConstraints, loadMap) {
 11    const res = {};
 12    const fd = await fs.promises.open(fileName, "r");
 13
 14    const b = Buffer.allocUnsafe(4);
 15    await fd.read(b, 0, 4, 0);
 16
 17    if (b.toString() != "r1cs") assert(false, "Invalid File format");
 18
 19    let p=4;
 20
 21    let v = await readU32();
 22
 23    if (v>1) assert(false, "Version not supported");
 24
 25    const nSections = await readU32();
 26
 27    let pHeader;
 28    let pConstraints;
 29    let headerSize;
 30    let constraintsSize;
 31    let pMap;
 32    let mapSize;
 33    for (let i=0; i<nSections; i++) {
 34        let ht = await readU32();
 35        let hl = await readU32();
 36        if (ht == 1) {
 37            if (typeof pHeader != "undefined") assert(false, "File has two headder sections");
 38            pHeader = p;
 39            headerSize = hl;
 40        } else if (ht==2) {
 41            if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections");
 42            pConstraints = p;
 43            constraintsSize = hl;
 44        } else if (ht==3) {
 45            pMap = p;
 46            mapSize = hl;
 47        }
 48        p += hl;
 49    }
 50
 51    if (typeof pHeader == "undefined") assert(false, "File has two header");
 52
 53    // Read Header
 54    p = pHeader;
 55    const fieldDefSize = await readU32();
 56    const pFieldDef = p;
 57
 58    const defType = await readU32();
 59    if (defType != 1) if (typeof pConstraints != "undefined") assert(false, "Field type not supported");
 60
 61    res.prime = await readBigInt();
 62
 63    if ( p != pFieldDef + fieldDefSize) assert("Invalid fieldDef size");
 64
 65    const bigIntFormat = await readU32();
 66    if (bigIntFormat != 0) assert(false, "BigInt format not supported");
 67
 68    const idSize = await readU32();
 69    if (idSize != 4) assert(false, "idSize not supported. Mus be 4");
 70
 71    res.nWires = await readU32();
 72    res.nPubOuts = await readU32();
 73    res.nPubIns = await readU32();
 74    res.nPrvIns = await readU32();
 75    res.nLabels = await readU32();
 76    res.nConstraints = await readU32();
 77
 78    if (p != pHeader + headerSize) assert(false, "Invalid header section size");
 79
 80    if (loadConstraints) {
 81        // Read Constraints
 82        p = pConstraints;
 83
 84        res.constraints = [];
 85        for (let i=0; i<res.nConstraints; i++) {
 86            const c = await readConstraint();
 87            res.constraints.push(c);
 88        }
 89        if (p != pConstraints + constraintsSize) assert(false, "Invalid constraints size");
 90    }
 91
 92    // Read Labels
 93
 94    if (loadMap) {
 95        p = pMap;
 96
 97        res.map = [];
 98        for (let i=0; i<res.nLabels; i++) {
 99            const idx = await readU32();
100            res.map.push(idx);
101        }
102        if (p != pMap + mapSize) assert(false, "Invalid Map size");
103    }
104
105    await fd.close();
106
107    return res;
108
109    async function readU32() {
110        const b = Buffer.allocUnsafe(4);
111        await fd.read(b, 0, 4, p);
112
113        p+=4;
114
115        return b.readInt32LE(0);
116    }
117
118    async function readBigInt() {
119        const bl = Buffer.allocUnsafe(1);
120        await fd.read(bl, 0, 1, p);
121        p++;
122
123        const l = bl[0];
124        const b = Buffer.allocUnsafe(l);
125        await fd.read(b, 0, l, p);
126        p += l;
127
128        const arr = Uint8Array.from(b);
129
130        const arrr = new Array(arr.length);
131        for (let i=0; i<arr.length; i++) {
132            arrr[i] = arr[arr.length-1-i];
133        }
134
135        const n = bigInt.fromArray(arrr, 256);
136
137        return n;
138    }
139
140    async function readConstraint() {
141        const c = {};
142        c.a = await readLC();
143        c.b = await readLC();
144        c.c = await readLC();
145        return c;
146    }
147
148    async function readLC() {
149        const lc= {};
150        const nIdx = await readU32();
151        for (let i=0; i<nIdx; i++) {
152            const idx = await readU32();
153            const val = await readBigInt();
154            lc[idx] = val;
155        }
156        return lc;
157    }
158}
159
160async function buildR1cs(ctx, fileName) {
161
162    const fd = await fs.promises.open(fileName, "w");
163
164
165    await fd.write("r1cs"); // Magic "r1cs"
166
167    let p = 4;
168    await writeU32(1); // Version
169    await writeU32(3); // Number of Sections
170
171    // Write the header
172    ///////////
173    await writeU32(1); // Header type
174    const pHeaderSize = p;
175    await writeU32(0); // Temporally set to 0 length
176
177
178    // Field Def
179    const pFieldDefSize = p;
180    await writeU32(0); // Temporally set to 0 length
181    await writeU32(1);
182    await writeBigInt(ctx.field.p);
183    const fieldDefSize = p - pFieldDefSize - 4;
184
185    await writeU32(0);  // Variable bigInt format
186    await writeU32(4);  // Id Size
187
188    const NWires =
189        ctx.totals[ctx.stONE] +
190        ctx.totals[ctx.stOUTPUT] +
191        ctx.totals[ctx.stPUBINPUT] +
192        ctx.totals[ctx.stPRVINPUT] +
193        ctx.totals[ctx.stINTERNAL];
194
195    await writeU32(NWires);
196    await writeU32(ctx.totals[ctx.stOUTPUT]);
197    await writeU32(ctx.totals[ctx.stPUBINPUT]);
198    await writeU32(ctx.totals[ctx.stPRVINPUT]);
199    await writeU32(ctx.signals.length);
200    await writeU32(ctx.constraints.length);
201
202    const headerSize = p - pHeaderSize - 4;
203
204    // Write constraints
205    ///////////
206    await writeU32(2); // Constraints type
207    const pConstraintsSize = p;
208    await writeU32(0); // Temporally set to 0 length
209
210    for (let i=0; i<ctx.constraints.length; i++) {
211        if ((ctx.verbose)&&(i%10000 == 0)) {
212            if (ctx.verbose) console.log("writing constraint: ", i);
213            await fd.datasync();
214        }
215        await writeConstraint(ctx.constraints[i]);
216    }
217
218    const constraintsSize = p - pConstraintsSize - 4;
219
220    // Write map
221    ///////////
222    await writeU32(3); // wires2label type
223    const pMapSize = p;
224    await writeU32(0); // Temporally set to 0 length
225
226
227    const arr = new Array(NWires);
228    for (let i=0; i<ctx.signals.length; i++) {
229        const outIdx = ctx.signals[i].id;
230        if (ctx.signals[i].e>=0) continue;     // If has an alias, continue..
231        assert(typeof outIdx  != "undefined", `Signal ${i} does not have index`);
232        if (outIdx>=NWires) continue; // Is a constant or a discarded variable
233        if (typeof arr[ctx.signals[i].id] == "undefined") {
234            arr[outIdx] = i;
235        }
236    }
237    for (let i=0; i<arr.length; i++) {
238        await writeU32(arr[i]);
239        if ((ctx.verbose)&&(i%100000)) console.log("writing label2wire map: ", i);
240    }
241
242    const mapSize = p - pMapSize -4;
243
244    // Write sizes
245    await writeU32(headerSize, pHeaderSize);
246    await writeU32(fieldDefSize, pFieldDefSize);
247    await writeU32(constraintsSize, pConstraintsSize);
248    await writeU32(mapSize, pMapSize);
249
250    await fd.sync();
251    await fd.close();
252
253    async function writeU32(v, pos) {
254        const b = Buffer.allocUnsafe(4);
255        b.writeInt32LE(v);
256
257        await fd.write(b, 0, 4, pos);
258
259        if (typeof(pos) == "undefined") p += 4;
260    }
261
262    async function writeConstraint(c) {
263        await writeLC(c.a);
264        await writeLC(c.b);
265        await writeLC(lc.negate(c.c));
266    }
267
268    async function writeLC(lc) {
269        const idxs = Object.keys(lc.values);
270        await writeU32(idxs.length);
271        for (let s in lc.values) {
272            let lSignal = ctx.signals[s];
273
274            while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
275
276            await writeU32(lSignal.id);
277            await writeBigInt(lc.values[s]);
278        }
279    }
280
281    async function writeBigInt(n) {
282
283        const bytes = bigInt(n).toArray(256).value.reverse();
284
285        await fd.write(Buffer.from([bytes.length, ...bytes ]));
286
287        p += bytes.length+1;
288    }
289}