arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/compiler.js
src/compiler.jsRaw
  1
  2const fs = require("fs");
  3const path = require("path");
  4const bigInt = require("big-integer");
  5const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
  6const __MASK__ = new bigInt(2).pow(253).minus(1);
  7const assert = require("assert");
  8const gen = require("./gencode");
  9const exec = require("./exec");
 10const lc = require("./lcalgebra");
 11
 12module.exports = compile;
 13
 14const parser = require("../jaz.js").parser;
 15
 16function compile(srcFile) {
 17
 18    return new Promise ((resolve, reject) => {
 19        const fullFileName = srcFile;
 20        const fullFilePath = path.dirname(fullFileName);
 21
 22        const src = fs.readFileSync(fullFileName, "utf8");
 23        const ast = parser.parse(src);
 24
 25        assert(ast.type == "BLOCK");
 26
 27        const ctx = {
 28            scopes: [{}],
 29            signals: {
 30                one: {
 31                    fullName: "one",
 32                    value: bigInt(1),
 33                    equivalence: "",
 34                    direction: ""
 35                }
 36            },
 37            currentComponent: "",
 38            constrains: [],
 39            components: {},
 40            templates: {},
 41            functions: {},
 42            functionParams: {},
 43            filePath: fullFilePath,
 44            fileName: fullFileName
 45        };
 46
 47        exec(ctx, ast);
 48
 49        reduceConstrains(ctx);
 50        generateWitnessNames(ctx);
 51
 52        if (ctx.error) {
 53            reject(ctx.error);
 54        }
 55
 56        ctx.scopes = [{}];
 57
 58        const mainCode = gen(ctx,ast);
 59        if (ctx.error) reject(ctx.error);
 60
 61        const def = buildCircuitDef(ctx, mainCode);
 62
 63        resolve(def);
 64    });
 65}
 66
 67
 68function generateWitnessNames(ctx) {
 69
 70    const totals = {
 71        "output": 0,
 72        "pubInput": 0,
 73        "one": 0,
 74        "prvInput": 0,
 75        "internal": 0,
 76        "constant": 0,
 77    };
 78    const ids = {};
 79
 80    function priorize(t1, t2) {
 81        if ((t1 == "error") || (t2=="error")) return "error";
 82        if (t1 == "internal") {
 83            return t2;
 84        } else if (t2=="internal") {
 85            return t1;
 86        }
 87        if ((t1 == "one") || (t2 == "one")) return "one";
 88        if ((t1 == "constant") || (t2 == "constant")) return "constant";
 89        if (t1!=t2) return "error";
 90        return t1;
 91    }
 92
 93    // First classify the signals
 94    for (let s in ctx.signals) {
 95        const signal = ctx.signals[s];
 96        let tAll = "internal";
 97        let lSignal = signal;
 98        let end = false;
 99        while (!end) {
100            let t = lSignal.category || "internal";
101            if (s == "one") {
102                t = "one";
103            } else if (lSignal.value) {
104                t = "constant";
105            } else if (lSignal.component=="main") {
106                if (lSignal.direction == "IN") {
107                    if (lSignal.private) {
108                        t = "prvInput";
109                    } else {
110                        t = "pubInput";
111                    }
112                } else if (lSignal.direction == "OUT") {
113                    t = "output";
114                }
115            }
116            tAll = priorize(t,tAll);
117            if (lSignal.equivalence) {
118                lSignal = ctx.signals[lSignal.equivalence];
119            } else {
120                end=true;
121            }
122        }
123        if (tAll == "error") {
124            throw new Error("Incompatible types in signal: " + s);
125        }
126        if (lSignal.category) totals[lSignal.category]--;
127        lSignal.category = tAll;
128        totals[lSignal.category] ++;
129    }
130
131    ids["one"] = 0;
132    ids["output"] = 1;
133    ids["pubInput"] = ids["output"] + totals["output"];
134    ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
135    ids["internal"] = ids["prvInput"] + totals["prvInput"];
136    ids["constant"] = ids["internal"] + totals["internal"];
137    const nSignals = ids["constant"] + totals["constant"];
138
139    ctx.signalNames = new Array(nSignals);
140    for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
141    ctx.signalName2Idx = {};
142
143    for (let s in ctx.signals) {
144        const signal = ctx.signals[s];
145        let lSignal = signal;
146        while (lSignal.equivalence) {
147            lSignal = ctx.signals[lSignal.equivalence];
148        }
149        if ( typeof(lSignal.id) === "undefined" ) {
150            lSignal.id = ids[lSignal.category] ++;
151        }
152
153        signal.id = lSignal.id;
154        ctx.signalNames[signal.id].push(signal.fullName);
155        ctx.signalName2Idx[signal.fullName] = signal.id;
156    }
157
158    ctx.totals = totals;
159}
160
161function reduceConstrains(ctx) {
162    const newConstrains = [];
163    for (let i=0; i<ctx.constrains.length; i++) {
164        const c = lc.canonize(ctx, ctx.constrains[i]);
165        if (!lc.isZero(c)) {
166            newConstrains.push(c);
167        }
168    }
169    ctx.constrains = newConstrains;
170}
171
172
173function buildCircuitDef(ctx, mainCode) {
174    const res = {
175        mainCode: mainCode
176    };
177    res.signalName2Idx = ctx.signalName2Idx;
178
179    res.components = [];
180    res.componentName2Idx = {};
181    for (let c in ctx.components) {
182        const idCoponent = res.components.length;
183        res.components.push({
184            name: c,
185            params: ctx.components[c].params,
186            template: ctx.components[c].template,
187            inputSignals: 0
188        });
189        res.componentName2Idx[c] = idCoponent;
190    }
191
192    res.signals = new Array(ctx.signalNames.length);
193    for (let i=0; i<ctx.signalNames.length; i++) {
194        res.signals[i] = {
195            names: ctx.signalNames[i],
196            triggerComponents: []
197        };
198        ctx.signalNames[i].map( (fullName) => {
199            const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
200            if (ctx.signals[fullName].direction == "IN") {
201                res.signals[i].triggerComponents.push(idComponet);
202                res.components[idComponet].inputSignals++;
203            }
204        });
205    }
206
207    res.constrains = buildConstrains(ctx);
208
209    res.templates = ctx.templates;
210
211    res.functions = {};
212    for (let f in ctx.functions) {
213        res.functions[f] = {
214            params: ctx.functionParams[f],
215            func: ctx.functions[f]
216        };
217    }
218
219    res.nPrvInputs = ctx.totals.prvInput;
220    res.nPubInputs = ctx.totals.pubInput;
221    res.nInputs = res.nPrvInputs + res.nPubInputs;
222    res.nOutputs = ctx.totals.output;
223    res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
224    res.nConstants = ctx.totals.constant;
225    res.nSignals = res.nVars + res.nConstants;
226
227    return res;
228}
229
230/*
231    Build constrains
232
233A constrain like this
234
235      [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0        ] = 0
236      [     5*s2 + 6*s3] * [ s2 +     ] - [s0 + 2* s2] = 0
237      [s1 +          s3] * [ s2 + 5*s3] - [s4        ] = 0
238
239is converted to
240
241[
242    [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1"         }],
243    [{        "2":"5","3":"6"} , {"2":"1"        } , {"0":"1", "2":"2"}],
244    [{"1":"1",        "3":"1"} , {"2":"1","3":"5"} , {"4":"1"         }]
245]
246           ^                            ^                  ^
247           |                            |                  |
248           A                            B                  C
249
250*/
251
252function buildConstrains(ctx) {
253    const res = [];
254
255    function fillLC(dst, src) {
256        for (let s in src.values) {
257            const v = src.values[s].toString();
258            const id = ctx.signalName2Idx[s];
259            dst[id] = v;
260        }
261    }
262
263    for (let i=0; i<ctx.constrains.length; i++) {
264        const A = {};
265        const B = {};
266        const C = {};
267
268        fillLC(A, ctx.constrains[i].a);
269        fillLC(B, ctx.constrains[i].b);
270        fillLC(C, lc.negate(ctx.constrains[i].c));
271
272        res.push([A,B,C]);
273    }
274
275    return res;
276}
277
278
279