"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable no-await-in-loop */
/* eslint-disable no-console */
const core_1 = require("@maci-protocol/core");
const domainobjs_1 = require("@maci-protocol/domainobjs");
const config_1 = require("hardhat/config");
const fs_1 = __importDefault(require("fs"));
const logger_1 = require("../../ts/logger");
const ContractStorage_1 = require("../helpers/ContractStorage");
const Deployment_1 = require("../helpers/Deployment");
const ProofGenerator_1 = require("../helpers/ProofGenerator");
const types_1 = require("../helpers/types");
/**
 * Prove hardhat task for generating off-chain proofs and sending them on-chain
 */
(0, config_1.task)("prove", "Command to generate proofs")
    .addParam("poll", "The poll id", undefined, config_1.types.string)
    .addParam("outputDir", "Output directory for proofs", undefined, config_1.types.string)
    .addParam("coordinatorPrivateKey", "Coordinator maci private key", undefined, config_1.types.string)
    .addOptionalParam("rapidsnark", "Rapidsnark binary path", undefined, config_1.types.string)
    .addOptionalParam("messageProcessorWitnessGenerator", "MessageProcessor witness generator binary path", undefined, config_1.types.string)
    .addParam("tallyFile", "The file to store the tally proof", undefined, config_1.types.string)
    .addOptionalParam("voteTallyWitnessGenerator", "VoteTally witness generator binary path", undefined, config_1.types.string)
    .addOptionalParam("stateFile", "The file with the serialized maci state", undefined, config_1.types.string)
    .addOptionalParam("startBlock", "The block number to start fetching logs from", undefined, config_1.types.int)
    .addOptionalParam("blocksPerBatch", "The number of blocks to fetch logs from", undefined, config_1.types.int)
    .addOptionalParam("endBlock", "The block number to stop fetching logs from", undefined, config_1.types.int)
    .addOptionalParam("transactionHash", "The transaction hash of the first transaction", undefined, config_1.types.int)
    .addOptionalParam("ipfsMessageBackupFiles", "Backup files for ipfs messages (name format: ipfsHash1.json, ipfsHash2.json, ..., ipfsHashN.json)", undefined, config_1.types.string)
    .setAction(async ({ outputDir, poll, coordinatorPrivateKey, stateFile, rapidsnark, messageProcessorWitnessGenerator, voteTallyWitnessGenerator, tallyFile, startBlock, blocksPerBatch, endBlock, transactionHash, ipfsMessageBackupFiles, }, hre) => {
    const deployment = Deployment_1.Deployment.getInstance();
    deployment.setHre(hre);
    const storage = ContractStorage_1.ContractStorage.getInstance();
    // if we do not have the output directory just create it
    const isOutputDirExists = fs_1.default.existsSync(outputDir);
    if (!isOutputDirExists) {
        // Create the directory
        await fs_1.default.promises.mkdir(outputDir, { recursive: true });
    }
    const maciPrivateKey = domainobjs_1.PrivateKey.deserialize(coordinatorPrivateKey);
    const coordinatorKeypair = new domainobjs_1.Keypair(maciPrivateKey);
    const signer = await deployment.getDeployer();
    const { network } = hre;
    const startBalance = await signer.provider.getBalance(signer);
    (0, logger_1.logMagenta)({ text: (0, logger_1.info)(`Start balance: ${Number(startBalance / 10n ** 12n) / 1e6}`) });
    const maciContractAddress = storage.mustGetAddress(types_1.EContracts.MACI, network.name);
    const maciContract = await deployment.getContract({ name: types_1.EContracts.MACI, address: maciContractAddress });
    const pollContracts = await maciContract.polls(poll);
    const pollContract = await deployment.getContract({
        name: types_1.EContracts.Poll,
        address: pollContracts.poll,
    });
    const isStateAqMerged = await pollContract.stateMerged();
    // Check that the state and message trees have been merged for at least the first poll
    if (!isStateAqMerged && poll.toString() === "0") {
        throw new Error("The state tree has not been merged yet. Please use the mergeSignups subcommand to do so.");
    }
    const maciState = await ProofGenerator_1.ProofGenerator.prepareState({
        maciContract,
        pollContract,
        maciPrivateKey,
        coordinatorKeypair,
        pollId: poll,
        signer,
        outputDir,
        ipfsMessageBackupFiles: ipfsMessageBackupFiles?.split(/\s*,\s*/),
        options: {
            stateFile,
            transactionHash,
            startBlock,
            endBlock,
            blocksPerBatch,
        },
    });
    const foundPoll = maciState.polls.get(BigInt(poll));
    if (!foundPoll) {
        throw new Error(`Poll ${poll} not found`);
    }
    const modeKeys = {
        [core_1.EMode.QV]: "qv",
        [core_1.EMode.NON_QV]: "nonQv",
        [core_1.EMode.FULL]: "full",
    };
    const mode = deployment.getDeployConfigField(types_1.EContracts.Poll, "mode") ?? core_1.EMode.QV;
    const voteTallyZkey = deployment.getDeployConfigField(types_1.EContracts.VerifyingKeysRegistry, `zkeys.${modeKeys[mode]}.voteTallyZkey`, true);
    const voteTallyWasm = deployment.getDeployConfigField(types_1.EContracts.VerifyingKeysRegistry, `zkeys.${modeKeys[mode]}.voteTallyWasm`, true);
    const messageProcessorZkey = deployment.getDeployConfigField(types_1.EContracts.VerifyingKeysRegistry, `zkeys.${modeKeys[mode]}.messageProcessorZkey`, true);
    const messageProcessorWasm = deployment.getDeployConfigField(types_1.EContracts.VerifyingKeysRegistry, `zkeys.${modeKeys[mode]}.messageProcessorWasm`, true);
    const proofGenerator = new ProofGenerator_1.ProofGenerator({
        poll: foundPoll,
        maciContractAddress,
        tallyContractAddress: pollContracts.tally,
        rapidsnark,
        tally: {
            zkey: voteTallyZkey,
            witnessGenerator: voteTallyWitnessGenerator,
            wasm: voteTallyWasm,
        },
        messageProcessor: {
            zkey: messageProcessorZkey,
            witnessGenerator: messageProcessorWitnessGenerator,
            wasm: messageProcessorWasm,
        },
        outputDir,
        tallyOutputFile: tallyFile,
        mode,
    });
    const data = {
        processProofs: [],
        tallyProofs: [],
    };
    data.processProofs = await proofGenerator.generateMpProofs();
    data.tallyProofs = await proofGenerator
        .generateTallyProofs(network.name, network.config.chainId?.toString())
        .then(({ proofs }) => proofs);
    const endBalance = await signer.provider.getBalance(signer);
    (0, logger_1.logMagenta)({ text: (0, logger_1.info)(`End balance: ${Number(endBalance / 10n ** 12n) / 1e6}`) });
    (0, logger_1.logMagenta)({ text: (0, logger_1.info)(`Prove expenses: ${Number((startBalance - endBalance) / 10n ** 12n) / 1e6}`) });
    (0, logger_1.logMagenta)({
        text: (0, logger_1.info)("Please make sure that you do not delete the proofs from the proof directory until they are all submitted on-chain.\nRegenerating proofs will result in overwriting the existing proofs and commitments which will be different due to the use of random salts."),
    });
});
//# sourceMappingURL=prove.js.map