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