"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractVerifyingKey = exports.verifyProof = exports.generateProofRapidSnark = exports.generateProofSnarkjs = void 0;
exports.readProofs = readProofs;
const crypto_1 = require("@maci-protocol/crypto");
const snarkjs_1 = require("snarkjs");
const child_process_1 = __importDefault(require("child_process"));
const fs_1 = __importDefault(require("fs"));
const os_1 = require("os");
const path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
/**
 * Generate a zk-SNARK proof using snarkjs
 * @param inputs - the inputs to the circuit
 * @param zkeyPath - the path to the zkey
 * @param wasmPath - the path to the wasm witness
 * @returns the zk-SNARK proof and public signals
 */
const generateProofSnarkjs = async ({ inputs, zkeyPath, wasmPath, }) => {
    if (!wasmPath) {
        throw new Error("wasmPath must be specified");
    }
    const { proof, publicSignals } = await snarkjs_1.groth16.fullProve(inputs, wasmPath, zkeyPath);
    return { proof, publicSignals };
};
exports.generateProofSnarkjs = generateProofSnarkjs;
/**
 * Generate a zk-SNARK proof using rapidnsark
 * @param inputs - the inputs to the circuit
 * @param zkeyPath - the path to the zkey
 * @param witnessExePath - the path to the compiled witness binary
 * @param rapidsnarkExePath - the path to the rapidnsark binary
 * @returns the zk-SNARK proof and public signals
 */
const generateProofRapidSnark = async ({ inputs, zkeyPath, witnessExePath, rapidsnarkExePath, }) => {
    // intel chip flow (use rapidnsark)
    // Create tmp directory
    const tmpPath = path_1.default.resolve((0, os_1.tmpdir)(), `tmp-${Date.now()}`);
    await fs_1.default.promises.mkdir(tmpPath, { recursive: true });
    const inputJsonPath = path_1.default.resolve(tmpPath, "input.json");
    const outputWtnsPath = path_1.default.resolve(tmpPath, "output.wtns");
    const proofJsonPath = path_1.default.resolve(tmpPath, "proof.json");
    const publicJsonPath = path_1.default.resolve(tmpPath, "public.json");
    // Write input.json
    const jsonData = JSON.stringify((0, crypto_1.stringifyBigInts)(inputs));
    await fs_1.default.promises.writeFile(inputJsonPath, jsonData);
    const { promisify } = await Promise.resolve().then(() => __importStar(require("util")));
    const execFile = promisify(child_process_1.default.execFile);
    // Generate the witness
    await execFile(witnessExePath, [inputJsonPath, outputWtnsPath]);
    const isOutputWtnsExists = fs_1.default.existsSync(outputWtnsPath);
    if (!isOutputWtnsExists) {
        throw new Error(`Error executing ${witnessExePath} ${inputJsonPath} ${outputWtnsPath}`);
    }
    // Generate the proof
    await execFile(rapidsnarkExePath, [zkeyPath, outputWtnsPath, proofJsonPath, publicJsonPath]);
    const isProofJsonPathExists = fs_1.default.existsSync(proofJsonPath);
    if (!isProofJsonPathExists) {
        throw new Error(`Error executing ${rapidsnarkExePath} ${zkeyPath} ${outputWtnsPath} ${proofJsonPath} ${publicJsonPath}`);
    }
    // Read the proof and public inputs
    const proof = JSON.parse(await fs_1.default.promises.readFile(proofJsonPath).then((res) => res.toString()));
    const publicSignals = JSON.parse(await fs_1.default.promises.readFile(publicJsonPath).then((res) => res.toString()));
    // remove all artifacts
    await Promise.all([proofJsonPath, publicJsonPath, inputJsonPath, outputWtnsPath].map(utils_1.unlinkFile));
    // remove tmp directory
    await fs_1.default.promises.rmdir(tmpPath);
    return { proof, publicSignals };
};
exports.generateProofRapidSnark = generateProofRapidSnark;
/**
 * Verify a zk-SNARK proof using snarkjs
 * @param publicInputs - the public inputs to the circuit
 * @param proof - the proof
 * @param verifyingKey - the verification key
 * @param cleanup - whether to cleanup the threads or not
 * @returns whether the proof is valid or not
 */
const verifyProof = async (publicInputs, proof, verifyingKey, cleanup = true) => {
    const isValid = await snarkjs_1.groth16.verify(verifyingKey, publicInputs, proof);
    if (cleanup) {
        await (0, utils_1.cleanThreads)();
    }
    return isValid;
};
exports.verifyProof = verifyProof;
/**
 * Extract the Verification Key from a zKey
 * @param zkeyPath - the path to the zKey
 * @param cleanup - whether to cleanup the threads or not
 * @returns the verification key
 */
const extractVerifyingKey = async (zkeyPath, cleanup = true) => snarkjs_1.zKey
    .exportVerificationKey(zkeyPath)
    .then((verifyingKey) => verifyingKey)
    .finally(() => {
    if (cleanup) {
        (0, utils_1.cleanThreads)();
    }
});
exports.extractVerifyingKey = extractVerifyingKey;
/**
 * Read and parse proofs
 *
 * @param args - read proofs arguments
 * @returns proofs
 */
async function readProofs({ files, folder, type }) {
    return Promise.all(files
        .filter((f) => f.startsWith(`${type}_`) && f.endsWith(".json"))
        .sort((a, b) => {
        const numA = parseInt(a.match(/\d+/)?.[0] ?? "0", 10);
        const numB = parseInt(b.match(/\d+/)?.[0] ?? "0", 10);
        return numA - numB;
    })
        .map(async (file) => fs_1.default.promises.readFile(`${folder}/${file}`, "utf8").then((result) => JSON.parse(result))));
}
//# sourceMappingURL=proofs.js.map