You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

532 lines
16 KiB

4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
6 years ago
5 years ago
6 years ago
4 years ago
6 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
6 years ago
4 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. Copyright 2018 0KIMS association.
  3. This file is part of circom (Zero Knowledge Circuit Compiler).
  4. circom is a free software: you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. circom is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with circom. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. const bigInt = require("big-integer");
  16. const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
  17. const sONE = 0;
  18. const buildC = require("./c_build");
  19. const constructionPhase = require("./construction_phase");
  20. const Ctx = require("./ctx");
  21. const ZqField = require("fflib").ZqField;
  22. const utils = require("./utils");
  23. const buildR1cs = require("./r1csfile").buildR1cs;
  24. module.exports = compile;
  25. async function compile(srcFile, options) {
  26. options.p = options.p || __P__;
  27. if (!options) {
  28. options = {};
  29. }
  30. if (typeof options.reduceConstraints === "undefined") {
  31. options.reduceConstraints = true;
  32. }
  33. const ctx = new Ctx();
  34. ctx.field = new ZqField(options.p);
  35. ctx.verbose= options.verbose || false;
  36. ctx.mainComponent = options.mainComponent || "main";
  37. constructionPhase(ctx, srcFile);
  38. console.log("NConstraints Before: "+ctx.constraints.length);
  39. if (ctx.error) {
  40. throw(ctx.error);
  41. }
  42. if (ctx.getComponentIdx(ctx.mainComponent)<0) {
  43. throw new Error("A main component must be defined");
  44. }
  45. if (ctx.verbose) console.log("Classify Signals");
  46. classifySignals(ctx);
  47. if (ctx.verbose) console.log("Reduce Constants");
  48. reduceConstants(ctx);
  49. if (options.reduceConstraints) {
  50. if (ctx.verbose) console.log("Reduce Constraints");
  51. // Repeat while reductions are performed
  52. let oldNConstrains = -1;
  53. while (ctx.constraints.length != oldNConstrains) {
  54. console.log("Reducing constraints: "+ctx.constraints.length);
  55. oldNConstrains = ctx.constraints.length;
  56. reduceConstrains(ctx);
  57. }
  58. }
  59. console.log("NConstraints After: "+ctx.constraints.length);
  60. generateWitnessNames(ctx);
  61. if (ctx.error) {
  62. throw(ctx.error);
  63. }
  64. if (options.cSourceWriteStream) {
  65. const cSrc = buildC(ctx);
  66. options.cSourceWriteStream.write(cSrc);
  67. }
  68. // const mainCode = gen(ctx,ast);
  69. if (ctx.error) throw(ctx.error);
  70. if (options.r1csFileName) {
  71. await buildR1cs(ctx, options.r1csFileName);
  72. }
  73. if (options.symWriteStream) {
  74. buildSyms(ctx, options.symWriteStream);
  75. }
  76. // const def = buildCircuitDef(ctx, mainCode);
  77. }
  78. function classifySignals(ctx) {
  79. const ERROR = 0xFFFF;
  80. function priorize(t1, t2) {
  81. if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
  82. if (t1 == ctx.stINTERNAL) {
  83. return t2;
  84. } else if (t2==ctx.stINTERNAL) {
  85. return t1;
  86. }
  87. if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
  88. if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
  89. if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
  90. if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
  91. if (t1!=t2) return ERROR;
  92. return t1;
  93. }
  94. // First classify the signals
  95. for (let s in ctx.signals) {
  96. const signal = ctx.signals[s];
  97. let tAll = ctx.stINTERNAL;
  98. let lSignal = signal;
  99. let end = false;
  100. while (!end) {
  101. let t = lSignal.c || ctx.stINTERNAL;
  102. if (s == 0) {
  103. t = ctx.stONE;
  104. } else if (lSignal.o & ctx.MAIN) {
  105. if (lSignal.o & ctx.IN) {
  106. if (lSignal.o & ctx.PRV) {
  107. t = ctx.stPRVINPUT;
  108. } else {
  109. t = ctx.stPUBINPUT;
  110. }
  111. } else if (lSignal.o & ctx.OUT) {
  112. t = ctx.stOUTPUT;
  113. }
  114. } else if (utils.isDefined(lSignal.v)) {
  115. t = ctx.stCONSTANT;
  116. }
  117. tAll = priorize(t,tAll);
  118. if (lSignal.e>=0) {
  119. lSignal = ctx.signals[lSignal.e];
  120. } else {
  121. end=true;
  122. }
  123. }
  124. if (tAll == ERROR) {
  125. throw new Error("Incompatible types in signal: " + s);
  126. }
  127. lSignal.c = tAll;
  128. }
  129. }
  130. function generateWitnessNames(ctx) {
  131. const totals = {};
  132. totals[ctx.stONE] = 0;
  133. totals[ctx.stOUTPUT] = 0;
  134. totals[ctx.stPUBINPUT] = 0;
  135. totals[ctx.stPRVINPUT] = 0;
  136. totals[ctx.stINTERNAL] = 0;
  137. totals[ctx.stDISCARDED] = 0;
  138. totals[ctx.stCONSTANT] = 0;
  139. const ids = {};
  140. // First classify the signals
  141. for (let s=0; s<ctx.signals.length; s++) {
  142. if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
  143. const signal = ctx.signals[s];
  144. let lSignal = signal;
  145. while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
  146. if (!( lSignal.o & ctx.COUNTED) ) {
  147. lSignal.o |= ctx.COUNTED;
  148. totals[lSignal.c] ++;
  149. }
  150. }
  151. ids[ctx.stONE] = 0;
  152. ids[ctx.stOUTPUT] = 1;
  153. ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
  154. ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
  155. ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
  156. ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
  157. ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
  158. const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
  159. for (let s=0; s<ctx.signals.length; s++) {
  160. if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
  161. const signal = ctx.signals[s];
  162. let lSignal = signal;
  163. while (lSignal.e>=0) {
  164. lSignal = ctx.signals[lSignal.e];
  165. }
  166. if ( typeof(lSignal.id) === "undefined" ) {
  167. lSignal.id = ids[lSignal.c] ++;
  168. }
  169. signal.id = lSignal.id;
  170. }
  171. ctx.totals = totals;
  172. }
  173. function reduceConstants(ctx) {
  174. const newConstraints = [];
  175. for (let i=0; i<ctx.constraints.length; i++) {
  176. if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
  177. const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
  178. if (!ctx.lc.isZero(c)) {
  179. newConstraints.push(c);
  180. }
  181. delete ctx.constraints[i];
  182. }
  183. ctx.constraints = newConstraints;
  184. }
  185. function reduceConstrains(ctx) {
  186. indexVariables();
  187. let possibleConstraints = Object.keys(ctx.constraints);
  188. let ii=0;
  189. while (possibleConstraints.length>0) {
  190. let nextPossibleConstraints = {};
  191. for (let i in possibleConstraints) {
  192. ii++;
  193. if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
  194. if (!ctx.constraints[i]) continue;
  195. const c = ctx.constraints[i];
  196. // Swap a and b if b has more variables.
  197. if (Object.keys(c.b).length > Object.keys(c.a).length) {
  198. const aux = c.a;
  199. c.a=c.b;
  200. c.b=aux;
  201. }
  202. // Mov to C if possible.
  203. if (isConstant(c.a)) {
  204. const ct = {t: "N", v: c.a.coefs[sONE]};
  205. c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
  206. c.a = { t: "LC", coefs: {} };
  207. c.b = { t: "LC", coefs: {} };
  208. }
  209. if (isConstant(c.b)) {
  210. const ct = {t: "N", v: c.b.coefs[sONE]};
  211. c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
  212. c.a = { t: "LC", coefs: {} };
  213. c.b = { t: "LC", coefs: {} };
  214. }
  215. if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
  216. const isolatedSignal = getFirstInternalSignal(ctx, c.c);
  217. if (isolatedSignal) {
  218. let lSignal = ctx.signals[isolatedSignal];
  219. while (lSignal.e>=0) {
  220. lSignal = ctx.signals[lSignal.e];
  221. }
  222. const isolatedSignalEquivalence = {
  223. t: "LC",
  224. coefs: {}
  225. };
  226. const invCoef = c.c.coefs[isolatedSignal].modInv(__P__);
  227. for (const s in c.c.coefs) {
  228. if (s != isolatedSignal) {
  229. const v = __P__.minus(c.c.coefs[s]).times(invCoef).mod(__P__);
  230. if (!v.isZero()) {
  231. isolatedSignalEquivalence.coefs[s] = v;
  232. }
  233. }
  234. }
  235. for (let j in lSignal.inConstraints) {
  236. if ((j!=i)&&(ctx.constraints[j])) {
  237. ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
  238. linkSignalsConstraint(j);
  239. if (j<i) {
  240. nextPossibleConstraints[j] = true;
  241. }
  242. }
  243. }
  244. ctx.constraints[i] = null;
  245. lSignal.c = ctx.stDISCARDED;
  246. } else {
  247. if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
  248. }
  249. }
  250. }
  251. possibleConstraints = Object.keys(nextPossibleConstraints);
  252. }
  253. unindexVariables();
  254. // Pack the constraints
  255. let o = 0;
  256. for (let i=0; i<ctx.constraints.length; i++) {
  257. if (ctx.constraints[i]) {
  258. if (o != i) {
  259. ctx.constraints[o] = ctx.constraints[i];
  260. }
  261. o++;
  262. }
  263. }
  264. ctx.constraints.length = o;
  265. function indexVariables() {
  266. for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
  267. }
  268. function linkSignalsConstraint(cidx) {
  269. const ct = ctx.constraints[cidx];
  270. for (let k in ct.a.coefs) linkSignal(k, cidx);
  271. for (let k in ct.b.coefs) linkSignal(k, cidx);
  272. for (let k in ct.c.coefs) linkSignal(k, cidx);
  273. }
  274. function unindexVariables() {
  275. for (let s in ctx.signals) {
  276. let lSignal = ctx.signals[s];
  277. while (lSignal.e>=0) {
  278. lSignal = ctx.signals[lSignal.e];
  279. }
  280. if (lSignal.inConstraints) delete lSignal.inConstraints;
  281. }
  282. }
  283. /*
  284. function unlinkSignal(signalName, cidx) {
  285. let lSignal = ctx.signals[signalName];
  286. while (lSignal.e>=0) {
  287. lSignal = ctx.signals[lSignal.e];
  288. }
  289. if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
  290. delete lSignal.inConstraints[cidx];
  291. }
  292. }
  293. */
  294. function linkSignal(signalName, cidx) {
  295. let lSignal = ctx.signals[signalName];
  296. while (lSignal.e>=0) {
  297. lSignal = ctx.signals[lSignal.e];
  298. }
  299. if (!lSignal.inConstraints) lSignal.inConstraints = {};
  300. lSignal.inConstraints[cidx] = true;
  301. }
  302. function getFirstInternalSignal(ctx, l) {
  303. for (let k in l.coefs) {
  304. const signal = ctx.signals[k];
  305. if (signal.c == ctx.stINTERNAL) return k;
  306. }
  307. return null;
  308. }
  309. function isConstant(l) {
  310. for (let k in l.coefs) {
  311. if ((k != sONE) && (!l.coefs[k].isZero())) return false;
  312. }
  313. if (!l.coefs[sONE] || l.coefs[sONE].isZero()) return false;
  314. return true;
  315. }
  316. }
  317. /*
  318. function buildCircuitDef(ctx, mainCode) {
  319. const res = {
  320. mainCode: mainCode
  321. };
  322. res.signalName2Idx = ctx.signalName2Idx;
  323. res.components = [];
  324. res.componentName2Idx = {};
  325. for (let c in ctx.components) {
  326. const idCoponent = res.components.length;
  327. res.components.push({
  328. name: c,
  329. params: ctx.components[c].params,
  330. template: ctx.components[c].template,
  331. inputSignals: 0
  332. });
  333. res.componentName2Idx[c] = idCoponent;
  334. }
  335. res.signals = new Array(ctx.signalNames.length);
  336. for (let i=0; i<ctx.signalNames.length; i++) {
  337. res.signals[i] = {
  338. names: ctx.signalNames[i],
  339. triggerComponents: []
  340. };
  341. ctx.signalNames[i].map( (fullName) => {
  342. const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
  343. if (ctx.signals[fullName].direction == "IN") {
  344. res.signals[i].triggerComponents.push(idComponet);
  345. res.components[idComponet].inputSignals++;
  346. }
  347. });
  348. }
  349. res.constraints = buildConstraints(ctx);
  350. res.templates = ctx.templates;
  351. res.functions = {};
  352. for (let f in ctx.functions) {
  353. res.functions[f] = {
  354. params: ctx.functionParams[f],
  355. func: ctx.functions[f]
  356. };
  357. }
  358. res.nPrvInputs = ctx.totals.prvInput;
  359. res.nPubInputs = ctx.totals.pubInput;
  360. res.nInputs = res.nPrvInputs + res.nPubInputs;
  361. res.nOutputs = ctx.totals.output;
  362. res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
  363. res.nConstants = ctx.totals.constant;
  364. res.nSignals = res.nVars + res.nConstants;
  365. return res;
  366. }
  367. */
  368. /*
  369. Build constraints
  370. A constraint like this
  371. [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0 ] = 0
  372. [ 5*s2 + 6*s3] * [ s2 + ] - [s0 + 2* s2] = 0
  373. [s1 + s3] * [ s2 + 5*s3] - [s4 ] = 0
  374. is converted to
  375. [
  376. [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1" }],
  377. [{ "2":"5","3":"6"} , {"2":"1" } , {"0":"1", "2":"2"}],
  378. [{"1":"1", "3":"1"} , {"2":"1","3":"5"} , {"4":"1" }]
  379. ]
  380. ^ ^ ^
  381. | | |
  382. A B C
  383. */
  384. function buildConstraints(ctx) {
  385. const res = [];
  386. function fillLC(dst, src) {
  387. if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
  388. for (let s in src.coefs) {
  389. const v = src.coefs[s].toString();
  390. const id = ctx.signalName2Idx[s];
  391. dst[id] = v;
  392. }
  393. }
  394. for (let i=0; i<ctx.constraints.length; i++) {
  395. const A = {};
  396. const B = {};
  397. const C = {};
  398. fillLC(A, ctx.constraints[i].a);
  399. fillLC(B, ctx.constraints[i].b);
  400. fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
  401. res.push([A,B,C]);
  402. }
  403. return res;
  404. }
  405. function buildSyms(ctx, strm) {
  406. let nSyms;
  407. addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
  408. function addSymbolsComponent(prefix, idComponet) {
  409. for (let n in ctx.components[idComponet].names.o) {
  410. const entrie = ctx.components[idComponet].names.o[n];
  411. addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
  412. }
  413. }
  414. function addSymbolArray(prefix, type, sizes, offset) {
  415. if (sizes.length==0) {
  416. if (type == "S") {
  417. let s=offset;
  418. while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
  419. let wId = ctx.signals[s].id;
  420. if (typeof(wId) == "undefined") wId=-1;
  421. strm.write(`${offset},${wId},${prefix}\n`);
  422. nSyms ++;
  423. if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
  424. } else {
  425. addSymbolsComponent(prefix+".", offset);
  426. }
  427. return 1;
  428. } else {
  429. let acc = 0;
  430. for (let i=0; i<sizes[0]; i++) {
  431. acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
  432. }
  433. return acc;
  434. }
  435. }
  436. }