/* eslint-disable node/no-missing-import */ import { ethers, keccak256 } from "ethers"; import Contract from "circomlib/src/evmasm"; import * as constants from "circomlib/src/poseidon_constants"; const N_ROUNDS_F = 8; const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63]; export function createCode(nInputs: number) { if (nInputs < 1 || nInputs > 8) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8"); const t = nInputs + 1; const nRoundsF = N_ROUNDS_F; const nRoundsP = N_ROUNDS_P[t - 2]; const C = new Contract(); function saveM() { for (let i = 0; i < t; i++) { for (let j = 0; j < t; j++) { C.push(constants.M[t - 2][i][j]); C.push((1 + i * t + j) * 32); C.mstore(); } } } function ark(r: number) { // st, q for (let i = 0; i < t; i++) { C.dup(t); // q, st, q C.push(constants.C[t - 2][r * t + i]); // K, q, st, q C.dup(2 + i); // st[i], K, q, st, q C.addmod(); // newSt[i], st, q C.swap(1 + i); // xx, st, q C.pop(); } } function sigma(p: number) { // sq, q C.dup(t); // q, st, q C.dup(1 + p); // st[p] , q , st, q C.dup(1); // q, st[p] , q , st, q C.dup(0); // q, q, st[p] , q , st, q C.dup(2); // st[p] , q, q, st[p] , q , st, q C.dup(0); // st[p] , st[p] , q, q, st[p] , q , st, q C.mulmod(); // st2[p], q, st[p] , q , st, q C.dup(0); // st2[p], st2[p], q, st[p] , q , st, q C.mulmod(); // st4[p], st[p] , q , st, q C.mulmod(); // st5[p], st, q C.swap(1 + p); C.pop(); // newst, q } function mix() { C.label("mix"); for (let i = 0; i < t; i++) { for (let j = 0; j < t; j++) { if (j === 0) { C.dup(i + t); // q, newSt, oldSt, q C.push((1 + i * t + j) * 32); C.mload(); // M, q, newSt, oldSt, q C.dup(2 + i + j); // oldSt[j], M, q, newSt, oldSt, q C.mulmod(); // acc, newSt, oldSt, q } else { C.dup(1 + i + t); // q, acc, newSt, oldSt, q C.push((1 + i * t + j) * 32); C.mload(); // M, q, acc, newSt, oldSt, q C.dup(3 + i + j); // oldSt[j], M, q, acc, newSt, oldSt, q C.mulmod(); // aux, acc, newSt, oldSt, q C.dup(2 + i + t); // q, aux, acc, newSt, oldSt, q C.swap(2); // acc, aux, q, newSt, oldSt, q C.addmod(); // acc, newSt, oldSt, q } } } for (let i = 0; i < t; i++) { C.swap(t - i + (t - i - 1)); C.pop(); } C.push(0); C.mload(); C.jmp(); } // Check selector C.push("0x0100000000000000000000000000000000000000000000000000000000"); C.push(0); C.calldataload(); C.div(); C.dup(0); C.push(keccak256(ethers.toUtf8Bytes(`poseidon(uint256[${nInputs}],uint256)`)).slice(0, 10)); // poseidon(uint256[n],uint256) C.eq(); C.swap(1); C.push(keccak256(ethers.toUtf8Bytes(`poseidon(bytes32[${nInputs}],bytes32)`)).slice(0, 10)); // poseidon(bytes32[n],bytes32) C.eq(); C.or(); C.jmpi("start"); C.invalid(); C.label("start"); saveM(); C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q // Load t values from the call data. // The function has a single array param param // [Selector (4)] [item1 (32)] [item2 (32)] .... [doman (32)] // Stack positions 0-nInputs. for (let i = 0; i < nInputs; i++) { C.push(0x04 + 0x20 * (nInputs - i - 1)); C.calldataload(); } C.push(0x04 + 0x20 * nInputs); C.calldataload(); for (let i = 0; i < nRoundsF + nRoundsP; i++) { ark(i); if (i < nRoundsF / 2 || i >= nRoundsP + nRoundsF / 2) { for (let j = 0; j < t; j++) { sigma(j); } } else { sigma(0); } const strLabel = "aferMix" + i; C._pushLabel(strLabel); C.push(0); C.mstore(); C.jmp("mix"); C.label(strLabel); } C.push("0x00"); C.mstore(); // Save it to pos 0; C.push("0x20"); C.push("0x00"); C.return(); mix(); return C.createTxData(); } export function generateABI(nInputs: number) { return [ { constant: true, inputs: [ { internalType: `bytes32[${nInputs}]`, name: "input", type: `bytes32[${nInputs}]`, }, { internalType: "bytes32", name: "domain", type: "bytes32", }, ], name: "poseidon", outputs: [ { internalType: "bytes32", name: "", type: "bytes32", }, ], payable: false, stateMutability: "pure", type: "function", }, { constant: true, inputs: [ { internalType: `uint256[${nInputs}]`, name: "input", type: `uint256[${nInputs}]`, }, { internalType: "uint256", name: "domain", type: "uint256", }, ], name: "poseidon", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], payable: false, stateMutability: "pure", type: "function", }, ]; }