UNPKG

zkverifyjs

Version:

Submit proofs to zkVerify and query proof state with ease using our npm package.

383 lines 14.2 kB
"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 __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.safeEmit = exports.extractErrorMessage = exports.getSelectedAccount = void 0; exports.waitForNodeToSync = waitForNodeToSync; exports.getProofProcessor = getProofProcessor; exports.getProofPallet = getProofPallet; exports.checkReadOnly = checkReadOnly; exports.interpretDryRunResponse = interpretDryRunResponse; exports.getKeyringAccountIfAvailable = getKeyringAccountIfAvailable; exports.isGroth16Config = isGroth16Config; exports.isPlonky2Config = isPlonky2Config; exports.isRisc0Config = isRisc0Config; exports.isUltraplonkConfig = isUltraplonkConfig; exports.isUltrahonkConfig = isUltrahonkConfig; exports.isVersionedUltrahonkConfig = isVersionedUltrahonkConfig; exports.isTeeConfig = isTeeConfig; exports.toSubmittableExtrinsic = toSubmittableExtrinsic; exports.validateHexString = validateHexString; const index_js_1 = require("../../config/index.js"); const index_js_2 = require("../transactions/errors/index.js"); __exportStar(require("./runtimeVersion/index.js"), exports); /** * Waits for the zkVerify node to finish syncing. Polls `system.health` at a * fixed interval up to a bounded deadline; throws on timeout or abort. * * @param api - The ApiPromise instance. * @param options - Optional timeout, poll interval, and abort signal. * @returns A promise that resolves when the node is synced. * @throws {Error} If the deadline is reached before the node finishes syncing. * @throws {DOMException} `AbortError` if the supplied signal is aborted. */ async function waitForNodeToSync(api, options = {}) { const { timeoutMs = 300000, pollIntervalMs = 1000, signal } = options; const deadline = Date.now() + timeoutMs; while (true) { if (signal?.aborted) { throw new DOMException('waitForNodeToSync aborted', 'AbortError'); } if (Date.now() > deadline) { throw new Error(`waitForNodeToSync timed out after ${timeoutMs}ms; node still reports isSyncing.`); } const health = await api.rpc.system.health(); if (!health.isSyncing.isTrue) return; await sleep(pollIntervalMs, signal); } } function sleep(ms, signal) { return new Promise((resolve, reject) => { if (signal?.aborted) { reject(new DOMException('sleep aborted', 'AbortError')); return; } const onAbort = () => { clearTimeout(timer); reject(new DOMException('sleep aborted', 'AbortError')); }; const timer = setTimeout(() => { signal?.removeEventListener('abort', onAbort); resolve(); }, ms); signal?.addEventListener('abort', onAbort, { once: true }); }); } function getProofProcessor(proofType) { const config = index_js_1.proofConfigurations[proofType]; if (!config) { throw new Error(`No config found for Proof Processor: ${proofType}`); } return config.processor; } function getProofPallet(proofType) { const config = index_js_1.proofConfigurations[proofType]; if (!config) { throw new Error(`No config found for Proof Pallet: ${proofType}`); } return config.pallet; } function checkReadOnly(connection) { if ((!('accounts' in connection) || ('accounts' in connection && connection.accounts.size === 0)) && !('injector' in connection)) { throw new Error('This action requires an active account. The session is currently in read-only mode because no account is associated with it. Please provide an account at session start, or add one to the current session using `addAccount`.'); } } let cachedVerifierPallets = null; function getVerifierPallets() { if (cachedVerifierPallets) return cachedVerifierPallets; const entries = Object.values(index_js_1.proofConfigurations ?? {}); cachedVerifierPallets = new Set(entries.map((cfg) => cfg.pallet).filter(Boolean)); return cachedVerifierPallets; } /** * Interprets a dry run response and returns whether it was successful and any error message. * @param api - The Polkadot.js API instance. * @param resultHex - The hex-encoded response from a dry run. * @param proofType * @returns An object containing `success` (boolean) and `message` (string). */ async function interpretDryRunResponse(api, resultHex, proofType) { const responseBytes = Uint8Array.from(Buffer.from(resultHex.replace(/^0x/, ''), 'hex')); if (responseBytes.length === 0) { return { success: false, type: 'unknown_error', message: 'Empty dryRun response', verificationError: false, }; } if (responseBytes[0] === 0x01) { let validityError; try { validityError = api.registry.createType('TransactionValidityError', responseBytes.slice(1)); } catch { try { validityError = api.registry.createType('SpRuntimeTransactionValidityError', responseBytes.slice(1)); } catch { validityError = null; } } if (validityError?.isInvalid) { const code = `InvalidTransaction.${validityError?.asInvalid.type}`; return { success: false, type: 'validity_error', code, message: code, verificationError: false, }; } if (validityError?.isUnknown) { const code = `UnknownTransaction.${validityError?.asUnknown.type}`; return { success: false, type: 'validity_error', code, message: code, verificationError: false, }; } return { success: false, type: 'validity_error', message: validityError?.toString() ?? 'TransactionValidityError', verificationError: false, }; } if (responseBytes[0] === 0x00 && responseBytes.length >= 2) { if (responseBytes[1] === 0x00) { return { success: true, type: 'ok', message: 'Optimistic Verification Successful!', }; } if (responseBytes[1] === 0x01) { try { const dispatchErrorCodec = api.registry.createType('DispatchError', responseBytes.slice(2)); const { code, message, section } = (0, index_js_2.decodeDispatchError)(api, dispatchErrorCodec); const expectedPallet = proofType ? index_js_1.proofConfigurations?.[proofType]?.pallet : undefined; const isVerifier = !!section && (section === expectedPallet || getVerifierPallets().has(section)); return { success: false, type: 'dispatch_error', code, message, verificationError: isVerifier, }; } catch (err) { const message = err instanceof Error ? err.message : String(err); return { success: false, type: 'exception', message, verificationError: false, }; } } } return { success: false, type: 'unknown_error', message: `Unexpected response format: ${resultHex}`, verificationError: false, }; } /** * Retrieves the selected account from the connection based on the provided account address. * If no account address is provided, it defaults to the first available account. * * @param {AccountConnection} connection - The connection containing account information. * @param {string | undefined} accountAddress - The optional account address to retrieve. * @returns {KeyringPair} - The selected account. * @throws {Error} If the account is not found. */ const getSelectedAccount = (connection, accountAddress) => { let selectedAccount; if (accountAddress) { selectedAccount = connection.accounts.get(accountAddress); } else { selectedAccount = Array.from(connection.accounts.values())[0]; } if (!selectedAccount) { throw new Error(`Account ${accountAddress ?? ''} not found in session.`); } return selectedAccount; }; exports.getSelectedAccount = getSelectedAccount; /** * Retrieves the selected `KeyringPair` from the given connection if it is an `AccountConnection`. * * - If the connection has local `accounts` (i.e., it's an `AccountConnection`), it uses the provided `accountAddress` * to select the appropriate account via `getSelectedAccount`. * - If the connection is a `WalletConnection`, returns `undefined`. * * @param {AccountConnection | WalletConnection} connection - The connection object which may contain accounts. * @param {string} [accountAddress] - Optional address of the account to select. * @returns {KeyringPair | undefined} - The selected `KeyringPair` if available, otherwise `undefined`. */ function getKeyringAccountIfAvailable(connection, accountAddress) { return 'accounts' in connection ? (0, exports.getSelectedAccount)(connection, accountAddress) : undefined; } /** * Extracts a human-readable error message from various error types. * * @param err - The error object to extract a message from. * @returns A string message describing the error. */ const extractErrorMessage = (err) => { if (err instanceof Error) { return err.message; } if (typeof err === 'object' && err !== null) { const maybeError = err; if (typeof maybeError.error === 'string') { return maybeError.error; } try { return JSON.stringify(err); } catch { return String(err); } } return String(err); }; exports.extractErrorMessage = extractErrorMessage; /** * Safe wrapper for emitting events without crashing. */ const safeEmit = (emitter, event, data) => { try { emitter.emit(event, data); } catch (error) { console.debug(`Failed to emit event ${event}:`, error); } }; exports.safeEmit = safeEmit; /** * Type guard for Groth16Config */ function isGroth16Config(options) { return (options.proofType === index_js_1.ProofType.groth16 && options.config !== undefined && options.config.library !== undefined && options.config.curve !== undefined); } /** * Type guard for Plonky2Config */ function isPlonky2Config(options) { return (options.proofType === index_js_1.ProofType.plonky2 && options.config !== undefined && options.config.hashFunction !== undefined); } /** * Type guard for Risc0Config */ function isRisc0Config(options) { return (options.proofType === index_js_1.ProofType.risc0 && options.config !== undefined && options.config.version !== undefined); } /** * Type guard for Ultraplonk Config */ function isUltraplonkConfig(options) { return (options.proofType === index_js_1.ProofType.ultraplonk && options.config !== undefined && options.config.numberOfPublicInputs !== undefined); } /** * Type guard for Ultrahonk Config */ function isUltrahonkConfig(options) { return (options.proofType === index_js_1.ProofType.ultrahonk && options.config !== undefined && options.config.variant !== undefined); } /** * Type guard for versioned Ultrahonk Config */ function isVersionedUltrahonkConfig(options) { return (isUltrahonkConfig(options) && options.config.version !== undefined); } /** * Type guard for TEE Config */ function isTeeConfig(options) { return (options.proofType === index_js_1.ProofType.tee && options.config !== undefined && options.config.variant !== undefined); } // ADD_NEW_PROOF_TYPE if it has a config options object /** * Type guard to check if an object is a SubmittableExtrinsic<'promise'>. * * A SubmittableExtrinsic is identified by the presence of a `signAsync` method. * * @param obj - The object to evaluate. * @returns True if the object is a SubmittableExtrinsic, otherwise false. */ function isSubmittableExtrinsic(obj) { return (typeof obj === 'object' && obj !== null && 'signAsync' in obj && typeof obj.signAsync === 'function'); } /** * Ensures the provided extrinsic is a SubmittableExtrinsic. * Converts a raw Extrinsic to Submittable if needed using the given API instance. * * @param extrinsic - A SubmittableExtrinsic or raw Extrinsic. * @param api - An instance of ApiPromise used to convert the extrinsic. * @returns A SubmittableExtrinsic<'promise'> ready to be signed and submitted. */ function toSubmittableExtrinsic(extrinsic, api) { if (isSubmittableExtrinsic(extrinsic)) { return extrinsic; } const call = api.createType('Call', extrinsic.method); return api.tx(call); } /** * Asserts that the given input is a 0x-prefixed hex string. * * @param input - The string to validate. * @returns The input unchanged, for ergonomic inline use. * @throws {Error} If the input does not start with `0x`. */ function validateHexString(input) { if (!input.startsWith('0x')) { throw new Error('Invalid format: string input must be 0x-prefixed.'); } return input; } //# sourceMappingURL=index.js.map