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.

213 lines
5.9 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. // Copyright (c) 2018 Jordi Baylina
  2. // License: LGPL-3.0+
  3. //
  4. const Contract = require("./evmasm");
  5. const { unstringifyBigInts } = require("ffjavascript").utils;
  6. const Web3Utils = require("web3-utils");
  7. const { C:K, M } = unstringifyBigInts(require("./poseidon_constants.json"));
  8. const N_ROUNDS_F = 8;
  9. const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63];
  10. function toHex256(a) {
  11. let S = a.toString(16);
  12. while (S.length < 64) S="0"+S;
  13. return "0x" + S;
  14. }
  15. function createCode(nInputs) {
  16. if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8");
  17. const t = nInputs + 1;
  18. const nRoundsF = N_ROUNDS_F;
  19. const nRoundsP = N_ROUNDS_P[t - 2];
  20. const C = new Contract();
  21. function saveM() {
  22. for (let i=0; i<t; i++) {
  23. for (let j=0; j<t; j++) {
  24. C.push(toHex256(M[t-2][j][i]));
  25. C.push((1+i*t+j)*32);
  26. C.mstore();
  27. }
  28. }
  29. }
  30. function ark(r) { // st, q
  31. for (let i=0; i<t; i++) {
  32. C.dup(t); // q, st, q
  33. C.push(toHex256(K[t-2][r*t+i])); // K, q, st, q
  34. C.dup(2+i); // st[i], K, q, st, q
  35. C.addmod(); // newSt[i], st, q
  36. C.swap(1 + i); // xx, st, q
  37. C.pop();
  38. }
  39. }
  40. function sigma(p) {
  41. // sq, q
  42. C.dup(t); // q, st, q
  43. C.dup(1+p); // st[p] , q , st, q
  44. C.dup(1); // q, st[p] , q , st, q
  45. C.dup(0); // q, q, st[p] , q , st, q
  46. C.dup(2); // st[p] , q, q, st[p] , q , st, q
  47. C.dup(0); // st[p] , st[p] , q, q, st[p] , q , st, q
  48. C.mulmod(); // st2[p], q, st[p] , q , st, q
  49. C.dup(0); // st2[p], st2[p], q, st[p] , q , st, q
  50. C.mulmod(); // st4[p], st[p] , q , st, q
  51. C.mulmod(); // st5[p], st, q
  52. C.swap(1+p);
  53. C.pop(); // newst, q
  54. }
  55. function mix() {
  56. C.label("mix");
  57. for (let i=0; i<t; i++) {
  58. for (let j=0; j<t; j++) {
  59. if (j==0) {
  60. C.dup(i+t); // q, newSt, oldSt, q
  61. C.push((1+i*t+j)*32);
  62. C.mload(); // M, q, newSt, oldSt, q
  63. C.dup(2+i+j); // oldSt[j], M, q, newSt, oldSt, q
  64. C.mulmod(); // acc, newSt, oldSt, q
  65. } else {
  66. C.dup(1+i+t); // q, acc, newSt, oldSt, q
  67. C.push((1+i*t+j)*32);
  68. C.mload(); // M, q, acc, newSt, oldSt, q
  69. C.dup(3+i+j); // oldSt[j], M, q, acc, newSt, oldSt, q
  70. C.mulmod(); // aux, acc, newSt, oldSt, q
  71. C.dup(2+i+t); // q, aux, acc, newSt, oldSt, q
  72. C.swap(2); // acc, aux, q, newSt, oldSt, q
  73. C.addmod(); // acc, newSt, oldSt, q
  74. }
  75. }
  76. }
  77. for (let i=0; i<t; i++) {
  78. C.swap((t -i) + (t -i-1));
  79. C.pop();
  80. }
  81. C.push(0);
  82. C.mload();
  83. C.jmp();
  84. }
  85. // Check selector
  86. C.push("0x0100000000000000000000000000000000000000000000000000000000");
  87. C.push(0);
  88. C.calldataload();
  89. C.div();
  90. C.dup(0);
  91. C.push(Web3Utils.keccak256(`poseidon(uint256[${nInputs}])`).slice(0, 10)); // poseidon(uint256[n])
  92. C.eq();
  93. C.swap(1);
  94. C.push(Web3Utils.keccak256(`poseidon(bytes32[${nInputs}])`).slice(0, 10)); // poseidon(bytes32[n])
  95. C.eq();
  96. C.or();
  97. C.jmpi("start");
  98. C.invalid();
  99. C.label("start");
  100. saveM();
  101. C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q
  102. // Load t values from the call data.
  103. // The function has a single array param param
  104. // [Selector (4)] [item1 (32)] [item2 (32)] ....
  105. // Stack positions 0-nInputs.
  106. for (let i=0; i<t; i++) {
  107. C.push(0x04+(0x20*(nInputs-i)));
  108. C.calldataload();
  109. }
  110. for (let i=0; i<nRoundsF+nRoundsP-1; i++) {
  111. ark(i);
  112. if ((i<nRoundsF/2) || (i>=nRoundsP+nRoundsF/2)) {
  113. for (let j=0; j<t; j++) {
  114. sigma(j);
  115. }
  116. } else {
  117. sigma(0);
  118. }
  119. const strLabel = "aferMix"+i;
  120. C._pushLabel(strLabel);
  121. C.push(0);
  122. C.mstore();
  123. C.jmp("mix");
  124. C.label(strLabel);
  125. }
  126. C.push(toHex256(K[t-2][(nRoundsF+nRoundsP-1)*t])); // K, st, q
  127. C.dup(t+1); // q, K, st, q
  128. C.swap(2); // st[0], K, q, st\st[0]
  129. C.addmod(); // st q
  130. sigma(0);
  131. C.push("0x00");
  132. C.mstore(); // Save it to pos 0;
  133. C.push("0x20");
  134. C.push("0x00");
  135. C.return();
  136. mix();
  137. return C.createTxData();
  138. }
  139. function generateABI(nInputs) {
  140. return [
  141. {
  142. "constant": true,
  143. "inputs": [
  144. {
  145. "internalType": `bytes32[${nInputs}]`,
  146. "name": "input",
  147. "type": `bytes32[${nInputs}]`
  148. }
  149. ],
  150. "name": "poseidon",
  151. "outputs": [
  152. {
  153. "internalType": "bytes32",
  154. "name": "",
  155. "type": "bytes32"
  156. }
  157. ],
  158. "payable": false,
  159. "stateMutability": "pure",
  160. "type": "function"
  161. },
  162. {
  163. "constant": true,
  164. "inputs": [
  165. {
  166. "internalType": `uint256[${nInputs}]`,
  167. "name": "input",
  168. "type": `uint256[${nInputs}]`
  169. }
  170. ],
  171. "name": "poseidon",
  172. "outputs": [
  173. {
  174. "internalType": "uint256",
  175. "name": "",
  176. "type": "uint256"
  177. }
  178. ],
  179. "payable": false,
  180. "stateMutability": "pure",
  181. "type": "function"
  182. }
  183. ];
  184. }
  185. module.exports.generateABI = generateABI;
  186. module.exports.createCode = createCode;