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.

627 lines
19 KiB

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