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