Skip to content

Commit

Permalink
fix ptau
Browse files Browse the repository at this point in the history
  • Loading branch information
dovgopoly committed May 14, 2024
1 parent 0973b74 commit 0c27242
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 92 deletions.
7 changes: 4 additions & 3 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ export type ManagerZKitConfig = {
circuitsDir: string;
artifactsDir: string;
verifiersDir: string;
ptauFile: string | null;
ptauDir: string;
allowDownload: boolean;
};

export const defaultManagerOptions: ManagerZKitConfig = {
export const defaultManagerOptions: Partial<ManagerZKitConfig> = {
circuitsDir: "circuits",
artifactsDir: "zkit-artifacts",
verifiersDir: "contracts/verifiers",
ptauFile: null,
allowDownload: true,
};

export type CompileOptions = {
Expand Down
2 changes: 1 addition & 1 deletion src/core/CircomZKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class CircomZKit {

let circuits = [] as string[];

readDirRecursively(circuitsDir, (dir: string, file: string) => {
readDirRecursively(circuitsDir, (_dir: string, file: string) => {
if (path.extname(file) == ".circom") {
circuits.push(path.relative(circuitsDir, file));
}
Expand Down
150 changes: 64 additions & 86 deletions src/core/ManagerZKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import * as readline from "readline";
import { v4 as uuid } from "uuid";

import { ManagerZKitConfig, ManagerZKitPrivateConfig, defaultManagerOptions } from "../config/config";
import { TemplateType } from "../types/types";
import { downloadFile } from "../utils/utils";
import { PtauInfo, TemplateType } from "../types/types";
import { downloadFile, readDirRecursively } from "../utils/utils";

/**
* `ManagerZKit` provides configuration options and utility methods used by the `CircomZKit` and `CircuitZKit` classes.
Expand All @@ -21,18 +21,16 @@ export class ManagerZKit {
* @param {Partial<ManagerZKitConfig>} [config=defaultManagerOptions] - The configuration options to use.
*/
constructor(config: Partial<ManagerZKitConfig> = defaultManagerOptions) {
const overriddenConfig: ManagerZKitConfig = { ...defaultManagerOptions, ...config };
const overriddenConfig = { ...defaultManagerOptions, ...config } as ManagerZKitConfig;

overriddenConfig.circuitsDir = path.join(process.cwd(), overriddenConfig.circuitsDir);
overriddenConfig.artifactsDir = path.join(process.cwd(), overriddenConfig.artifactsDir);
overriddenConfig.verifiersDir = path.join(process.cwd(), overriddenConfig.verifiersDir);

if (overriddenConfig.ptauFile) {
if (path.extname(overriddenConfig.ptauFile) != ".ptau") {
throw new Error('Ptau file must have ".ptau" extension.');
}

overriddenConfig.ptauFile = path.join(process.cwd(), overriddenConfig.ptauFile);
if (overriddenConfig.ptauDir) {
overriddenConfig.ptauDir = path.join(process.cwd(), overriddenConfig.ptauDir);
} else {
overriddenConfig.ptauDir = path.join(os.homedir(), ".zkit", ".ptau");
}

this._config = {
Expand All @@ -47,18 +45,27 @@ export class ManagerZKit {
/**
* Fetches the `ptau` file.
*
* @dev If the local `ptauFile` is not provided, the method will attempt to download it.
* The user will be prompted every time a download is required.
* @dev If `ptau` file is not found, this method will try to download it. Use `allowDownload=false` to disable this behavior.
*
* @param {number} minConstraints - The minimum number of constraints the `ptau` file must support.
* @returns {Promise<string>} The path to the `ptau` file.
*/
public async fetchPtauFile(minConstraints: number): Promise<string> {
if (this._config.ptauFile) {
return this._fetchLocalPtau();
const ptauId = Math.max(Math.ceil(Math.log2(minConstraints)), 8);

if (ptauId > 20) {
throw new Error(
'Circuit has too many constraints. The maximum number of constraints is 2^20. Consider passing "ptauDir=PATH_TO_LOCAL_DIR".',
);
}

const ptauInfo = this._searchPtau(ptauId);

if (ptauInfo.url) {
await this._downloadPtau(ptauInfo);
}

return this._fetchGlobalPtau(minConstraints);
return ptauInfo.file;
}

/**
Expand Down Expand Up @@ -89,23 +96,14 @@ export class ManagerZKit {
}

/**
* Returns the path to the local `ptau` file if configured.
*
* @returns {string | null} The path to the `ptau` file.
*/
public getPtauFile(): string | null {
return this._config.ptauFile;
}

/**
* Returns the path to the `ptau` download directory.
* Returns the path to the `ptau` directory.
*
* @dev The `ptau` download directory is located at `${HOME}/.zkit/.ptau`.
* @dev The default `ptau` directory is located at `${HOME}/.zkit/.ptau`.
*
* @returns {string} The path to the `ptau` download directory.
* @returns {string} The path to the `ptau` directory.
*/
public getPtauDir(): string {
return path.join(os.homedir(), ".zkit", ".ptau");
return this._config.ptauDir;
}

/**
Expand Down Expand Up @@ -144,105 +142,85 @@ export class ManagerZKit {
}

/**
* Fetches the `ptau` file from the global directory that supports the specified number of constraints.
* Downloads the `ptau` file if it doesn't exist.
* Returns whether the download of the `ptau` file is allowed.
*
* @param {number} minConstraints - The minimum number of constraints the `ptau` file must support.
* @returns {Promise<string>} The path to the `ptau` file.
* @returns {boolean} Whether the download of the `ptau` file is allowed.
*/
private async _fetchGlobalPtau(minConstraints: number): Promise<string> {
const ptauId = Math.max(Math.ceil(Math.log2(minConstraints)), 8);

if (ptauId > 20) {
throw new Error(
'Circuit has too many constraints. The maximum number of constraints is 2^20. Consider passing "ptau=PATH_TO_FILE".',
);
}

const ptauInfo = this._searchGlobalPtau(ptauId);

if (ptauInfo.url) {
if (!(await this._askForDownloadAllowance(ptauInfo.url))) {
throw new Error('Download is cancelled. Allow download or consider passing "ptauFile=PATH_TO_FILE"');
}

fs.mkdirSync(this.getPtauDir(), { recursive: true });

if (!(await downloadFile(ptauInfo.file, ptauInfo.url))) {
throw new Error("Something went wrong while downloading the ptau file.");
}
}

return ptauInfo.file;
public getAllowDownload(): boolean {
return this._config.allowDownload;
}

/**
* Fetches the `ptau` file from the local directory.
* Downloads the `ptau` file. The download is allowed only if the user confirms it.
*
* @returns {string} The path to the `ptau` file.
* @param {PtauInfo} ptauInfo - The `ptau` file and download url.
*/
private _fetchLocalPtau(): string {
if (!fs.existsSync(this._config.ptauFile!)) {
throw new Error(`Ptau file "${this._config.ptauFile!}" doesn't exist.`);
private async _downloadPtau(ptauInfo: PtauInfo): Promise<void> {
if (!this.getAllowDownload() && !(await this._askForDownloadAllowance(ptauInfo))) {
throw new Error('Download is cancelled. Allow download or consider passing "ptauDir=PATH_TO_LOCAL_DIR"');
}

return this._config.ptauFile!;
fs.mkdirSync(this.getPtauDir(), { recursive: true });

if (!(await downloadFile(ptauInfo.file, ptauInfo.url!))) {
throw new Error("Something went wrong while downloading the ptau file.");
}
}

/**
* Searches for the `ptau` file that supports the specified number of constraints.
*
* @param {number} ptauId - The `ptau` file id.
* @returns {{ file: string; url: string | null }} The `ptau` file path and download url if the file doesn't exist.
* @returns {PtauInfo} The `ptau` file path and download url if the file doesn't exist.
*/
private _searchGlobalPtau(ptauId: number): { file: string; url: string | null } {
let entries = [] as fs.Dirent[];
private _searchPtau(ptauId: number): PtauInfo {
let foundPtauId = 0;
let foundFile = "";

if (fs.existsSync(this.getPtauDir())) {
entries = fs.readdirSync(this.getPtauDir(), { withFileTypes: true });
}

const entry = entries.find((entry) => {
if (!entry.isFile()) {
return false;
}

const match = entry.name.match(/^powers-of-tau-(\d+)\.ptau$/);
readDirRecursively(this.getPtauDir(), (_dir: string, file: string) => {
const match = path.basename(file).match(/^powers-of-tau-(\d+)\.ptau$/);

if (!match) {
return false;
return;
}

const entryPtauId = parseInt(match[1]);

return ptauId <= entryPtauId;
if (entryPtauId >= ptauId && entryPtauId >= foundPtauId) {
foundPtauId = entryPtauId;
foundFile = file;
}
});

const file = path.join(this.getPtauDir(), entry ? entry.name : `powers-of-tau-${ptauId}.ptau`);
const url = entry
? null
: `https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${ptauId.toString().padStart(2, "0")}.ptau`;
const file = foundPtauId == 0 ? path.join(this.getPtauDir(), `powers-of-tau-${ptauId}.ptau`) : foundFile;
const url =
foundPtauId == 0
? `https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${ptauId.toString().padStart(2, "0")}.ptau`
: null;

return { file, url };
}

/**
* Prompts the user to allow the download of the `ptau` file.
*
* @param {string} url - The url to download the `ptau` file from.
* @param {PtauInfo} ptauInfo - The `ptau` file and download url.
* @returns {Promise<boolean>} Whether the download is allowed.
*/
private _askForDownloadAllowance(url: string): Promise<boolean> {
private _askForDownloadAllowance(ptauInfo: PtauInfo): Promise<boolean> {
return new Promise((resolve) => {
const readLine = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

readLine.question(`No ptau found. Press [Y] to download it from "${url}": `, (response) => {
readLine.close();
resolve(response.toUpperCase() == "Y");
});
readLine.question(
`No ptau found. Press [Y] to download it from "${ptauInfo.url!}" to ${ptauInfo.file}: `,
(response) => {
readLine.close();
resolve(response.toUpperCase() == "Y");
},
);
});
}
}
5 changes: 5 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ export type CircuitInfo = {
export type FileType = "r1cs" | "zkey" | "vkey" | "sym" | "json" | "wasm" | "sol";
export type DirType = "circuit" | "artifact" | "verifier";
export type TemplateType = "groth16";

export type PtauInfo = {
file: string;
url: string | null;
};
6 changes: 4 additions & 2 deletions test/CircomZKit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ jest.mock("readline", () => ({

describe("happy flow", function () {
test("happy flow", async () => {
const circom = CircomZKit.create({
const circom = new CircomZKit({
circuitsDir: "test/circuits",
artifactsDir: "test/zkit-artifacts",
verifiersDir: "test/verifiers"
verifiersDir: "test/verifiers",
ptauDir: "test/ptau",
allowDownload: false,
});

console.log(circom.getCircuits());
Expand Down

0 comments on commit 0c27242

Please sign in to comment.