arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/c_tester.js
src/c_tester.jsRaw
  1const chai = require("chai");
  2const assert = chai.assert;
  3
  4const fs = require("fs");
  5var tmp = require("tmp-promise");
  6const path = require("path");
  7const compiler = require("./compiler");
  8const util = require("util");
  9const exec = util.promisify(require("child_process").exec);
 10
 11const stringifyBigInts = require("./utils").stringifyBigInts;
 12const unstringifyBigInts = require("./utils").unstringifyBigInts;
 13const bigInt = require("big-integer");
 14const utils = require("./utils");
 15const loadR1cs = require("./r1csfile").loadR1cs;
 16const ZqField = require("fflib").ZqField;
 17
 18module.exports = c_tester;
 19
 20
 21async function  c_tester(circomFile, _options) {
 22    tmp.setGracefulCleanup();
 23
 24    const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
 25
 26    const baseName = path.basename(circomFile, ".circom");
 27    const options = Object.assign({}, _options);
 28
 29    options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
 30    options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
 31    options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
 32    await compiler(circomFile, options);
 33
 34    const cdir = path.join(__dirname, "..", "c");
 35    await exec("cp" +
 36               ` ${path.join(dir.path, baseName + ".cpp")}` +
 37               " /tmp/circuit.cpp"
 38    );
 39    await exec("g++" +
 40               ` ${path.join(cdir,  "main.cpp")}` +
 41               ` ${path.join(cdir,  "calcwit.cpp")}` +
 42               ` ${path.join(cdir,  "utils.cpp")}` +
 43               ` ${path.join(cdir,  "zqfield.cpp")}` +
 44               ` ${path.join(dir.path, baseName + ".cpp")} ` +
 45               ` -o ${path.join(dir.path, baseName)}` +
 46               ` -I ${cdir}` +
 47               " -lgmp -std=c++11 -DSANITY_CHECK"
 48    );
 49
 50    // console.log(dir.path);
 51    return new CTester(dir, baseName);
 52}
 53
 54class CTester {
 55
 56    constructor(dir, baseName) {
 57        this.dir=dir;
 58        this.baseName = baseName;
 59    }
 60
 61    async release() {
 62        await this.dir.cleanup();
 63    }
 64
 65    async calculateWitness(input) {
 66        await fs.promises.writeFile(
 67            path.join(this.dir.path, "in.json"),
 68            JSON.stringify(stringifyBigInts(input), null, 1)
 69        );
 70        await exec(`${path.join(this.dir.path, this.baseName)}` +
 71                   ` ${path.join(this.dir.path, "in.json")}` +
 72                   ` ${path.join(this.dir.path, "out.json")}`
 73        );
 74        const resStr = await fs.promises.readFile(
 75            path.join(this.dir.path, "out.json")
 76        );
 77
 78        const res = unstringifyBigInts(JSON.parse(resStr));
 79        return res;
 80    }
 81
 82    async loadSymbols() {
 83        if (this.symbols) return;
 84        this.symbols = {};
 85        const symsStr = await fs.promises.readFile(
 86            path.join(this.dir.path, this.baseName + ".sym"),
 87            "utf8"
 88        );
 89        const lines = symsStr.split("\n");
 90        for (let i=0; i<lines.length; i++) {
 91            const arr = lines[i].split(",");
 92            if (arr.length!=3) continue;
 93            this.symbols[arr[2]] = {
 94                idx: Number(arr[0]),
 95                idxWit: Number(arr[1])
 96            };
 97        }
 98    }
 99
100    async loadConstraints() {
101        const self = this;
102        if (this.constraints) return;
103        const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
104        self.field = new ZqField(r1cs.prime);
105        self.nWires = r1cs.nWires;
106        self.constraints = r1cs.constraints;
107    }
108
109    async assertOut(actualOut, expectedOut) {
110        const self = this;
111        if (!self.symbols) await self.loadSymbols();
112
113        checkObject("main", expectedOut);
114
115        function checkObject(prefix, eOut) {
116
117            if (Array.isArray(eOut)) {
118                for (let i=0; i<eOut.length; i++) {
119                    checkObject(prefix + "["+i+"]", eOut[i]);
120                }
121            } else if ((typeof eOut == "object")&&(eOut.constructor.name == "Object")) {
122                for (let k in eOut) {
123                    checkObject(prefix + "."+k, eOut[k]);
124                }
125            } else {
126                if (typeof self.symbols[prefix] == "undefined") {
127                    assert(false, "Output variable not defined: "+ prefix);
128                }
129                const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString();
130                const be = bigInt(eOut).toString();
131                assert.strictEqual(ba, be, prefix);
132            }
133        }
134    }
135
136    async getDecoratedOutput(witness) {
137        const self = this;
138        const lines = [];
139        if (!self.symbols) await self.loadSymbols();
140        for (let n in self.symbols) {
141            let v;
142            if (utils.isDefined(witness[self.symbols[n].idxWit])) {
143                v = witness[self.symbols[n].idxWit].toString();
144            } else {
145                v = "undefined";
146            }
147            lines.push(`${n} --> ${v}`);
148        }
149        return lines.join("\n");
150    }
151
152    async checkConstraints(witness) {
153        const self = this;
154        if (!self.constraints) await self.loadConstraints();
155        for (let i=0; i<self.constraints.length; i++) {
156            checkConstraint(self.constraints[i]);
157        }
158
159        function checkConstraint(constraint) {
160            const F = self.field;
161            const a = evalLC(constraint.a);
162            const b = evalLC(constraint.b);
163            const c = evalLC(constraint.c);
164
165            assert (F.sub(F.mul(a,b), c).isZero(), "Constraint doesn't match");
166        }
167
168        function evalLC(lc) {
169            const F = self.field;
170            let v = F.zero;
171            for (let w in lc) {
172                v = F.add(
173                    v,
174                    F.mul( lc[w], witness[w] )
175                );
176            }
177            return v;
178        }
179    }
180
181}
182
183