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