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.

662 lines
21 KiB

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