arnaucube
arnaucube/circomlibPublic Self-hosted repository
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