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.

661 lines
20 KiB

4 years ago
  1. const utils = require("../../src/utils");
  2. const assert = require("assert");
  3. const Scalar = require("ffjavascript").Scalar;
  4. const F1Field = require("ffjavascript").F1Field;
  5. const BigArray = require("../../src/bigarray");
  6. function ref2src(c) {
  7. if ((c[0] == "R")||(c[0] == "RI")) {
  8. return c[1];
  9. } else if (c[0] == "V") {
  10. return c[1].toString();
  11. } else if (c[0] == "C") {
  12. return `(ctx->circuit->constants + ${c[1]})`;
  13. } else if (c[0] == "CC") {
  14. return "__cIdx";
  15. } else {
  16. assert(false);
  17. }
  18. }
  19. class CodeBuilderC {
  20. constructor() {
  21. this.ops = [];
  22. }
  23. addComment(comment) {
  24. this.ops.push({op: "COMMENT", comment});
  25. }
  26. addBlock(block) {
  27. this.ops.push({op: "BLOCK", block});
  28. }
  29. calcOffset(dLabel, offsets) {
  30. this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
  31. }
  32. assign(dLabel, src, sOffset) {
  33. this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
  34. }
  35. getSubComponentOffset(dLabel, component, hash, hashLabel) {
  36. this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
  37. }
  38. getSubComponentSizes(dLabel, component, hash, hashLabel) {
  39. this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
  40. }
  41. getSignalOffset(dLabel, component, hash, hashLabel) {
  42. this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
  43. }
  44. getSignalSizes(dLabel, component, hash, hashLabel) {
  45. this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
  46. }
  47. setSignal(component, signal, value) {
  48. this.ops.push({op: "SETSIGNAL", component, signal, value});
  49. }
  50. getSignal(dLabel, component, signal) {
  51. this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
  52. }
  53. copyN(dLabel, offset, src, n) {
  54. this.ops.push({op: "COPYN", dLabel, offset, src, n});
  55. }
  56. copyNRet(src, n) {
  57. this.ops.push({op: "COPYNRET", src, n});
  58. }
  59. fieldOp(dLabel, fOp, params) {
  60. this.ops.push({op: "FOP", dLabel, fOp, params});
  61. }
  62. ret() {
  63. this.ops.push({op: "RET"});
  64. }
  65. addLoop(condLabel, body) {
  66. this.ops.push({op: "LOOP", condLabel, body});
  67. }
  68. addIf(condLabel, thenCode, elseCode) {
  69. this.ops.push({op: "IF", condLabel, thenCode, elseCode});
  70. }
  71. fnCall(fnName, retLabel, params) {
  72. this.ops.push({op: "FNCALL", fnName, retLabel, params});
  73. }
  74. checkConstraint(a, b, strErr) {
  75. this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
  76. }
  77. log(val) {
  78. this.ops.push({op: "LOG", val});
  79. }
  80. concat(cb) {
  81. this.ops.push(...cb.ops);
  82. }
  83. hasCode() {
  84. for (let i=0; i<this.ops.length; i++) {
  85. if (this.ops[i].op != "COMMENT") return true;
  86. }
  87. return false;
  88. }
  89. _buildOffset(offsets) {
  90. let rN=0;
  91. let S = "";
  92. offsets.forEach((o) => {
  93. if ((o[0][0] == "V") && (o[1][0]== "V")) {
  94. rN += o[0][1]*o[1][1];
  95. return;
  96. }
  97. let f="";
  98. if (o[0][0] == "V") {
  99. if (o[0][1]==0) return;
  100. f += o[0][1];
  101. } else if (o[0][0] == "RI") {
  102. if (o[0][1]==0) return;
  103. f += o[0][1];
  104. } else if (o[0][0] == "R") {
  105. f += `Fr_toInt(${o[0][1]})`;
  106. } else {
  107. assert(false);
  108. }
  109. if (o[1][0] == "V") {
  110. if (o[1][1]==0) return;
  111. if (o[1][1]>1) {
  112. f += "*" + o[1][1];
  113. }
  114. } else if (o[1][0] == "RS") {
  115. f += `*${o[1][1]}[${o[1][2]}]`;
  116. } else {
  117. assert(false);
  118. }
  119. if (S!="") S+= " + ";
  120. S += f;
  121. });
  122. if (rN>0) {
  123. S = `${rN} + ${S}`;
  124. }
  125. return S;
  126. }
  127. build(code) {
  128. this.ops.forEach( (o) => {
  129. if (o.op == "COMMENT") {
  130. code.push(`/* ${o.comment} */`);
  131. } else if (o.op == "BLOCK") {
  132. const codeBlock=[];
  133. o.block.build(codeBlock);
  134. code.push(utils.ident(codeBlock));
  135. } else if (o.op == "CALCOFFSETS") {
  136. code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`);
  137. } else if (o.op == "ASSIGN") {
  138. const oS = ref2src(o.sOffset);
  139. if (oS != "0") {
  140. code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`);
  141. } else {
  142. code.push(`${o.dLabel} = ${ref2src(o.src)};`);
  143. }
  144. } else if (o.op == "GETSUBCOMPONENTOFFSET") {
  145. code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  146. } else if (o.op == "GETSUBCOMPONENTSIZES") {
  147. code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  148. } else if (o.op == "GETSIGNALOFFSET") {
  149. code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  150. } else if (o.op == "GETSIGNALSIZES") {
  151. code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  152. } else if (o.op == "SETSIGNAL") {
  153. code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`);
  154. } else if (o.op == "GETSIGNAL") {
  155. code.push(`ctx->getSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel});`);
  156. } else if (o.op == "COPYN") {
  157. const oS = ref2src(o.offset);
  158. const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel;
  159. code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`);
  160. } else if (o.op == "COPYNRET") {
  161. code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`);
  162. } else if (o.op == "RET") {
  163. code.push("goto returnFunc;");
  164. } else if (o.op == "FOP") {
  165. let paramsS = "";
  166. for (let i=0; i<o.params.length; i++) {
  167. if (i>0) paramsS += ", ";
  168. paramsS += ref2src(o.params[i]);
  169. }
  170. code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`);
  171. } else if (o.op == "LOOP") {
  172. code.push(`while (Fr_isTrue(${o.condLabel})) {`);
  173. const body = [];
  174. o.body.build(body);
  175. code.push(utils.ident(body));
  176. code.push("}");
  177. } else if (o.op == "IF") {
  178. code.push(`if (Fr_isTrue(${o.condLabel})) {`);
  179. const thenCode = [];
  180. o.thenCode.build(thenCode);
  181. code.push(utils.ident(thenCode));
  182. if (o.elseCode) {
  183. code.push("} else {");
  184. const elseCode = [];
  185. o.elseCode.build(elseCode);
  186. code.push(utils.ident(elseCode));
  187. }
  188. code.push("}");
  189. } else if (o.op == "FNCALL") {
  190. code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`);
  191. } else if (o.op == "CHECKCONSTRAINT") {
  192. code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`);
  193. } else if (o.op == "LOG") {
  194. code.push(`ctx->log(${ref2src(o.val)});`);
  195. }
  196. });
  197. }
  198. }
  199. class FunctionBuilderC {
  200. constructor(name, instanceDef, type) {
  201. this.name = name;
  202. this.instanceDef = instanceDef;
  203. this.type = type; // "COMPONENT" or "FUNCTIOM"
  204. this.definedFrElements = [];
  205. this.definedIntElements = [];
  206. this.definedSizeElements = [];
  207. this.definedPFrElements = [];
  208. this.initializedElements = [];
  209. this.initializedSignalOffset = [];
  210. this.initializedSignalSizes = [];
  211. }
  212. defineFrElements(dLabel, size) {
  213. this.definedFrElements.push({dLabel, size});
  214. }
  215. defineIntElement(dLabel) {
  216. this.definedIntElements.push({dLabel});
  217. }
  218. defineSizesElement(dLabel) {
  219. this.definedSizeElements.push({dLabel});
  220. }
  221. definePFrElement(dLabel) {
  222. this.definedPFrElements.push({dLabel});
  223. }
  224. initializeFrElement(dLabel, offset, idConstant) {
  225. this.initializedElements.push({dLabel, offset, idConstant});
  226. }
  227. initializeSignalOffset(dLabel, component, hash, hashLabel) {
  228. this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
  229. }
  230. initializeSignalSizes(dLabel, component, hash, hashLabel) {
  231. this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
  232. }
  233. setParams(params) {
  234. this.params = params;
  235. }
  236. _buildHeader(code) {
  237. this.definedFrElements.forEach( (o) => {
  238. code.push(`FrElement ${o.dLabel}[${o.size}];`);
  239. });
  240. this.definedIntElements.forEach( (o) => {
  241. code.push(`int ${o.dLabel};`);
  242. });
  243. this.definedSizeElements.forEach( (o) => {
  244. code.push(`Circom_Sizes ${o.dLabel};`);
  245. });
  246. this.definedPFrElements.forEach( (o) => {
  247. code.push(`PFrElement ${o.dLabel};`);
  248. });
  249. this.initializedElements.forEach( (o) => {
  250. code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`);
  251. });
  252. this.initializedSignalOffset.forEach( (o) => {
  253. code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  254. });
  255. this.initializedSignalSizes.forEach( (o) => {
  256. code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
  257. });
  258. }
  259. _buildFooter(code) {
  260. }
  261. newCodeBuilder() {
  262. return new CodeBuilderC();
  263. }
  264. setBody(body) {
  265. this.body = body;
  266. }
  267. build(code) {
  268. code.push(
  269. "/*",
  270. this.instanceDef,
  271. "*/"
  272. );
  273. if (this.type=="COMPONENT") {
  274. code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`);
  275. } else if (this.type=="FUNCTION") {
  276. let sParams = "";
  277. for (let i=0;i<this.params.length;i++ ) sParams += `, PFrElement ${this.params[i]}`;
  278. code.push(`void ${this.name}(Circom_CalcWit *ctx, PFrElement __retValue ${sParams}) {`);
  279. } else {
  280. assert(false);
  281. }
  282. const fnCode = [];
  283. this._buildHeader(fnCode);
  284. this.body.build(fnCode);
  285. if (this.type=="COMPONENT") {
  286. fnCode.push("ctx->finished(__cIdx);");
  287. } else if (this.type=="FUNCTION") {
  288. fnCode.push("returnFunc: ;");
  289. } else {
  290. assert(false);
  291. }
  292. this._buildFooter(fnCode);
  293. code.push(utils.ident(fnCode));
  294. code.push("}");
  295. }
  296. }
  297. class BuilderC {
  298. constructor(p, verbose) {
  299. this.F = new F1Field(p);
  300. this.hashMaps={};
  301. this.componentEntriesTables=new BigArray();
  302. this.sizes ={};
  303. this.constants = [];
  304. this.functions = [];
  305. this.components = new BigArray();
  306. this.usedConstants = {};
  307. this.verbose = verbose;
  308. }
  309. setHeader(header) {
  310. this.header=header;
  311. }
  312. // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
  313. addHashMap(name, hm) {
  314. this.hashMaps[name] = hm;
  315. }
  316. addComponentEntriesTable(name, cet) {
  317. this.componentEntriesTables.push({
  318. name: name,
  319. cet: cet
  320. });
  321. }
  322. addSizes(name, accSizes) {
  323. this.sizes[name] = accSizes;
  324. }
  325. addConstant(c) {
  326. c = this.F.e(c);
  327. const cS = c.toString();
  328. if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
  329. this.constants.push(c);
  330. this.usedConstants[cS] = this.constants.length - 1;
  331. return this.constants.length - 1;
  332. }
  333. addFunction(fnBuilder) {
  334. this.functions.push(fnBuilder);
  335. }
  336. addComponent(component) {
  337. this.components.push(component);
  338. }
  339. setMapIsInput(map) {
  340. this.mapIsInput = map;
  341. }
  342. setWit2Sig(wit2sig) {
  343. this.wit2sig = wit2sig;
  344. }
  345. newComponentFunctionBuilder(name, instanceDef) {
  346. return new FunctionBuilderC(name, instanceDef, "COMPONENT");
  347. }
  348. newFunctionBuilder(name, instanceDef) {
  349. return new FunctionBuilderC(name, instanceDef, "FUNCTION");
  350. }
  351. // Body functions
  352. _buildHeader(code) {
  353. code.push(
  354. "#include \"circom.h\"",
  355. "#include \"calcwit.h\"",
  356. `#define NSignals ${this.header.NSignals}`,
  357. `#define NComponents ${this.header.NComponents}`,
  358. `#define NOutputs ${this.header.NOutputs}`,
  359. `#define NInputs ${this.header.NInputs}`,
  360. `#define NVars ${this.header.NVars}`,
  361. `#define __P__ "${this.header.P.toString()}"`,
  362. ""
  363. );
  364. }
  365. _buildHashMaps(code) {
  366. code.push("// Hash Maps ");
  367. for (let hmName in this.hashMaps ) {
  368. const hm = this.hashMaps[hmName];
  369. let c = `Circom_HashEntry ${hmName}[256] = {`;
  370. for (let i=0; i<256; i++) {
  371. c += i>0 ? "," : "";
  372. if (hm[i]) {
  373. c += `{0x${hm[i][0]}LL, ${hm[i][1]}} /* ${hm[i][2]} */`;
  374. } else {
  375. c += "{0,0}";
  376. }
  377. }
  378. c += "};";
  379. code.push(c);
  380. }
  381. }
  382. _buildComponentEntriesTables(code) {
  383. code.push("// Component Entry tables");
  384. for (let i=0; i< this.componentEntriesTables.length; i++) {
  385. if ((this.verbose)&&(i%100000 ==0)) console.log(`_buildComponentEntriesTables ${i}/${this.componentEntriesTables.length}`);
  386. const cetName = this.componentEntriesTables[i].name;
  387. const cet = this.componentEntriesTables[i].cet;
  388. code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`);
  389. for (let j=0; j<cet.length; j++) {
  390. const ty = cet[j].type == "S" ? "_typeSignal" : "_typeComponent";
  391. code.push(` ${j>0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`);
  392. }
  393. code.push("};");
  394. }
  395. }
  396. _buildSizes(code) {
  397. code.push("// Sizes");
  398. for (let sName in this.sizes) {
  399. const accSizes = this.sizes[sName];
  400. let c = `Circom_Size ${sName}[${accSizes.length}] = {`;
  401. for (let i=0; i<accSizes.length; i++) {
  402. if (i>0) c += ",";
  403. c += accSizes[i];
  404. }
  405. c += "};";
  406. code.push(c);
  407. }
  408. }
  409. _buildConstants(code) {
  410. const self = this;
  411. code.push("// Constants");
  412. code.push(`FrElement _constants[${self.constants.length}] = {`);
  413. for (let i=0; i<self.constants.length; i++) {
  414. if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildConstants ${i}/${this.constants.length}`);
  415. code.push((i>0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}");
  416. }
  417. code.push("};");
  418. function number2Code(n) {
  419. const minShort = self.F.neg(self.F.e("80000000"));
  420. const maxShort = self.F.e("7FFFFFFF", 16);
  421. if ( (self.F.geq(n, minShort))
  422. &&(self.F.leq(n, maxShort)))
  423. {
  424. if (self.F.geq(n, self.F.zero)) {
  425. return addShortMontgomeryPositive(n);
  426. } else {
  427. return addShortMontgomeryNegative(n);
  428. }
  429. }
  430. return addLongMontgomery(n);
  431. function addShortMontgomeryPositive(a) {
  432. return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
  433. }
  434. function addShortMontgomeryNegative(a) {
  435. const b = -Scalar.toNumber(self.F.neg(a));
  436. return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
  437. }
  438. function addLongMontgomery(a) {
  439. return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`;
  440. }
  441. function getLongString(a) {
  442. let S = "";
  443. const arr = Scalar.toArray(a, 0x100000000);
  444. for (let i=0; i<self.F.n64*2; i+=2) {
  445. const idx = arr.length-2-i;
  446. if (i>0) S = S + ",";
  447. if ( idx >=0) {
  448. let msb = arr[idx].toString(16);
  449. while (msb.length<8) msb = "0" + msb;
  450. let lsb = arr[idx+1].toString(16);
  451. while (lsb.length<8) lsb = "0" + lsb;
  452. S += "0x" + msb + lsb + "LL";
  453. } else {
  454. S += "0LL";
  455. }
  456. }
  457. return S;
  458. }
  459. function toMontgomery(a) {
  460. return self.F.mul(a, self.F.R);
  461. }
  462. }
  463. }
  464. _buildFunctions(code) {
  465. for (let i=0; i<this.functions.length; i++) {
  466. const cfb = this.functions[i];
  467. cfb.build(code);
  468. }
  469. }
  470. _buildComponents(code) {
  471. code.push("// Components");
  472. code.push(`Circom_Component _components[${this.components.length}] = {`);
  473. for (let i=0; i<this.components.length; i++) {
  474. if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildComponents ${i}/${this.components.length}`);
  475. const c = this.components[i];
  476. const sep = i>0 ? " ," : " ";
  477. code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`);
  478. }
  479. code.push("};");
  480. }
  481. _buildMapIsInput(code) {
  482. code.push("// mapIsInput");
  483. code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`);
  484. let line = "";
  485. for (let i=0; i<this.mapIsInput.length; i++) {
  486. if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildMapIsInput ${i}/${this.mapIsInput.length}`);
  487. line += i>0 ? ", " : " ";
  488. line += toHex(this.mapIsInput[i]);
  489. if (((i+1) % 64)==0) {
  490. code.push(" "+line);
  491. line = "";
  492. }
  493. }
  494. if (line != "") code.push(" "+line);
  495. code.push("};");
  496. function toHex(number) {
  497. if (number < 0) number = 0xFFFFFFFF + number + 1;
  498. let S=number.toString(16).toUpperCase();
  499. while (S.length<8) S = "0" + S;
  500. return "0x"+S;
  501. }
  502. }
  503. _buildWit2Sig(code) {
  504. code.push("// Witness to Signal Table");
  505. code.push(`int _wit2sig[${this.wit2sig.length}] = {`);
  506. let line = "";
  507. for (let i=0; i<this.wit2sig.length; i++) {
  508. if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildWit2Sig ${i}/${this.wit2sig.length}`);
  509. line += i>0 ? "," : " ";
  510. line += this.wit2sig[i];
  511. if (((i+1) % 64) == 0) {
  512. code.push(" "+line);
  513. line = "";
  514. }
  515. }
  516. if (line != "") code.push(" "+line);
  517. code.push("};");
  518. }
  519. _buildCircuitVar(code) {
  520. code.push(
  521. "// Circuit Variable",
  522. "Circom_Circuit _circuit = {" ,
  523. " NSignals,",
  524. " NComponents,",
  525. " NInputs,",
  526. " NOutputs,",
  527. " NVars,",
  528. " _wit2sig,",
  529. " _components,",
  530. " _mapIsInput,",
  531. " _constants,",
  532. " __P__",
  533. "};"
  534. );
  535. }
  536. async build(fd) {
  537. const encoder = new TextEncoder("utf-8");
  538. const code=new BigArray();
  539. this._buildHeader(code);
  540. this._buildSizes(code);
  541. this._buildConstants(code);
  542. this._buildHashMaps(code);
  543. this._buildComponentEntriesTables(code);
  544. this._buildFunctions(code);
  545. this._buildComponents(code);
  546. this._buildMapIsInput(code);
  547. this._buildWit2Sig(code);
  548. this._buildCircuitVar(code);
  549. await writeCode(code);
  550. async function writeCode(c) {
  551. if (c.push) {
  552. for (let i=0; i<c.length; i++) {
  553. await writeCode(c[i]);
  554. }
  555. } else if (typeof c === "string") {
  556. await fd.write(encoder.encode(c + "\n"));
  557. }
  558. }
  559. }
  560. }
  561. module.exports = BuilderC;