diff --git a/src/common.ts b/src/common.ts index 1e958d2..4016be2 100644 --- a/src/common.ts +++ b/src/common.ts @@ -6,6 +6,7 @@ import { ZqField } from 'ffjavascript' import poseidon from 'poseidon-lite' import { Identity } from '@semaphore-protocol/identity' +import axios from 'axios' /* This is the "Baby Jubjub" curve described here: @@ -61,3 +62,37 @@ export function shamirRecovery(x1: bigint, x2: bigint, y1: bigint, y2: bigint): export function calculateIdentityCommitment(identitySecret: bigint) { return poseidon([identitySecret]) } + +export function isValidUrl(str: string): boolean { + try { + new URL(str) + return true + } catch (_) { + return false + } +} + +export async function checkFileExistsOnWeb(url: string): Promise { + try { + await axios.head(url) + return true + } catch (error) { + return false + } +} + +export async function checkFileExists(path: string): Promise { + if (isValidUrl(path)) { + return checkFileExistsOnWeb(path) + } else { + + if (typeof process === 'undefined' && typeof window !== 'undefined') { + throw new Error( + 'not allowed to read local files from browser', + ) + } + + const fs = await import('fs') + return fs.existsSync(path) + } +} diff --git a/src/rln.ts b/src/rln.ts index f73a79e..8ffef06 100644 --- a/src/rln.ts +++ b/src/rln.ts @@ -1,6 +1,6 @@ import { Identity } from '@semaphore-protocol/identity' import { VerificationKey } from './types' -import { DEFAULT_MERKLE_TREE_DEPTH, calculateIdentitySecret, calculateSignalHash } from './common' +import { DEFAULT_MERKLE_TREE_DEPTH, calculateIdentitySecret, calculateSignalHash, checkFileExists } from './common' import { IRLNRegistry, ContractRLNRegistry } from './registry' import { MemoryCache, EvaluatedProof, ICache, Status } from './cache' import { IMessageIDCounter, MemoryMessageIDCounter } from './message-id-counter' @@ -224,6 +224,19 @@ export class RLN implements IRLN { let wasmFilePath: string | Uint8Array | undefined let finalZkeyPath: string | Uint8Array | undefined let verificationKey: VerificationKey | undefined + + if (typeof args.wasmFilePath === 'string' && !await checkFileExists(args.wasmFilePath)) { + throw new Error( + `the file does not exist at the path for \`wasmFilePath\`: wasmFilePath=${args.wasmFilePath}`, + ) + } + + if (typeof args.finalZkeyPath === 'string' && !await checkFileExists(args.finalZkeyPath)) { + throw new Error( + `the file does not exist at the path for \`finalZkeyPath\`: finalZkeyPath=${args.finalZkeyPath}`, + ) + } + // If `args.wasmFilePath`, `args.finalZkeyPath`, and `args.verificationKey` are not given, see if we have defaults that can be used if (args.wasmFilePath === undefined && args.finalZkeyPath === undefined && args.verificationKey === undefined) { const defaultParams = await getDefaultRLNParams(treeDepth) @@ -346,6 +359,19 @@ export class RLN implements IRLN { // If all params are not given, use the default let withdrawWasmFilePath: string | Uint8Array | undefined let withdrawFinalZkeyPath: string | Uint8Array | undefined + + if (typeof args.withdrawWasmFilePath === 'string' && !await checkFileExists(args.withdrawWasmFilePath)) { + throw new Error( + `the file does not exist at the path for \`withdrawWasmFilePath\`: withdrawWasmFilePath=${args.withdrawWasmFilePath}`, + ) + } + + if (typeof args.withdrawFinalZkeyPath === 'string' && !await checkFileExists(args.withdrawFinalZkeyPath)) { + throw new Error( + `the file does not exist at the path for \`withdrawFinalZkeyPath\`: withdrawFinalZkeyPath=${args.withdrawFinalZkeyPath}`, + ) + } + // If `args.withdrawWasmFilePath`, `args.finalZkeyPath`, see if we have defaults that can be used if (args.withdrawWasmFilePath === undefined && args.withdrawFinalZkeyPath === undefined) { const defaultParams = await getDefaultWithdrawParams() diff --git a/tests/rln.test.ts b/tests/rln.test.ts index 2049c81..5debc2e 100644 --- a/tests/rln.test.ts +++ b/tests/rln.test.ts @@ -74,6 +74,74 @@ describe("RLN", function () { ); }); + test("should fail when wasmFilePath doesn't exist on the web", async function () { + const wasmFilePath = "https://rln-trusted-setup-ceremony-pse-p0tion-production.s3.eu-central-1.amazonaws.com/404" + const finalZkeyPath = "https://rln-trusted-setup-ceremony-pse-p0tion-production.s3.eu-central-1.amazonaws.com/404" + await expect(async () => { + await RLN.createWithContractRegistry({ + rlnIdentifier: rlnIdentifierA, + treeDepth: treeDepthWithoutDefaultParams, + provider: fakeProvider, + contractAddress: fakeContractAddress, + wasmFilePath: wasmFilePath, + finalZkeyPath: finalZkeyPath + }); + }).rejects.toThrow( + `the file does not exist at the path for \`wasmFilePath\`: wasmFilePath=${wasmFilePath}` + ); + }); + + test("should fail when finalZkeyPath doesn't exist on the web", async function () { + const wasmFilePath = "https://rln-trusted-setup-ceremony-pse-p0tion-production.s3.eu-central-1.amazonaws.com/circuits/rln-20/RLN-20.wasm" + const finalZkeyPath = "https://rln-trusted-setup-ceremony-pse-p0tion-production.s3.eu-central-1.amazonaws.com/404" + await expect(async () => { + await RLN.createWithContractRegistry({ + rlnIdentifier: rlnIdentifierA, + treeDepth: treeDepthWithoutDefaultParams, + provider: fakeProvider, + contractAddress: fakeContractAddress, + wasmFilePath: wasmFilePath, + finalZkeyPath: finalZkeyPath + }); + }).rejects.toThrow( + `the file does not exist at the path for \`finalZkeyPath\`: finalZkeyPath=${finalZkeyPath}` + ); + }); + + test("should fail when wasmFilePath doesn't exist on local", async function () { + const wasmFilePath = "./404" + const finalZkeyPath = "./404" + await expect(async () => { + await RLN.createWithContractRegistry({ + rlnIdentifier: rlnIdentifierA, + treeDepth: treeDepthWithoutDefaultParams, + provider: fakeProvider, + contractAddress: fakeContractAddress, + wasmFilePath: wasmFilePath, + finalZkeyPath: finalZkeyPath + }); + }).rejects.toThrow( + `the file does not exist at the path for \`wasmFilePath\`: wasmFilePath=${wasmFilePath}` + ); + }); + + test("should fail when finalZkeyPath doesn't exist on local", async function () { + const wasmFilePath = "./package.json" + const finalZkeyPath = "./404" + await expect(async () => { + await RLN.createWithContractRegistry({ + rlnIdentifier: rlnIdentifierA, + treeDepth: treeDepthWithoutDefaultParams, + provider: fakeProvider, + contractAddress: fakeContractAddress, + wasmFilePath: wasmFilePath, + finalZkeyPath: finalZkeyPath + }); + }).rejects.toThrow( + `the file does not exist at the path for \`finalZkeyPath\`: finalZkeyPath=${finalZkeyPath}` + ); + }); + test("should fail to prove if no proving params is given as constructor arguments", async function () { const rln = await RLN.createWithContractRegistry({ rlnIdentifier: rlnIdentifierA, diff --git a/tsconfig.json b/tsconfig.json index 5a87121..299440c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES5", - "module": "ES6", + "module": "ES2020", "moduleResolution": "node", "allowJs": true, "allowSyntheticDefaultImports": true,