UNPKG

zkcloudworker

Version:
1,601 lines (1,588 loc) 92.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/node/index.js var index_exports = {}; __export(index_exports, { Cloud: () => Cloud, Devnet: () => Devnet, Lightnet: () => Lightnet, Local: () => Local, LocalCloud: () => LocalCloud, LocalStorage: () => LocalStorage, Mainnet: () => Mainnet, Memory: () => Memory, NftAPI: () => NftAPI, Storage: () => Storage, TinyContract: () => TinyContract, TokenAPI: () => TokenAPI, Zeko: () => Zeko, accountBalance: () => accountBalance, accountBalanceMina: () => accountBalanceMina, accountExists: () => accountExists, bigintFromBase56: () => bigintFromBase56, bigintFromBase64: () => bigintFromBase64, bigintToBase56: () => bigintToBase56, bigintToBase64: () => bigintToBase64, checkAddress: () => checkAddress, checkMinaZkappTransaction: () => checkMinaZkappTransaction, createIpfsURL: () => createIpfsURL, createTransactionPayloads: () => createTransactionPayloads, currentNetwork: () => currentNetwork, defaultToken: () => defaultToken, deserializeFields: () => deserializeFields, deserializeIndexedMerkleMap: () => deserializeIndexedMerkleMap, fee: () => fee, fetchMinaAccount: () => fetchMinaAccount, fetchMinaActions: () => fetchMinaActions, fieldFromBase56: () => fieldFromBase56, fieldFromBase64: () => fieldFromBase64, fieldToBase56: () => fieldToBase56, fieldToBase64: () => fieldToBase64, formatTime: () => formatTime, fromBase: () => fromBase, getAccountFromGraphQL: () => getAccountFromGraphQL, getAccountNonce: () => getAccountNonce, getBalanceFromGraphQL: () => getBalanceFromGraphQL, getCurrentNetwork: () => getCurrentNetwork, getDeployer: () => getDeployer, getNetworkIdHash: () => getNetworkIdHash, getNonce: () => getNonce, getPaymentTxsFromBlockBerry: () => getPaymentTxsFromBlockBerry, getTxStatusFast: () => getTxStatusFast, getZkAppFromBlockBerry: () => getZkAppFromBlockBerry, getZkAppTxFromBlockBerry: () => getZkAppTxFromBlockBerry, getZkAppTxsFromBlockBerry: () => getZkAppTxsFromBlockBerry, initBlockchain: () => initBlockchain, loadIndexedMerkleMap: () => loadIndexedMerkleMap, makeString: () => makeString, networks: () => networks, parseIndexedMapSerialized: () => parseIndexedMapSerialized, parseTransactionPayloads: () => parseTransactionPayloads, pinJSON: () => pinJSON, saveIndexedMerkleMap: () => saveIndexedMerkleMap, sendTx: () => sendTx, serializeFields: () => serializeFields, serializeIndexedMap: () => serializeIndexedMap, serializeTransaction: () => serializeTransaction, sleep: () => sleep, toBase: () => toBase, tokenBalance: () => tokenBalance, transactionParams: () => transactionParams, txStatusBlockberry: () => txStatusBlockberry, zkCloudWorker: () => zkCloudWorker, zkCloudWorkerClient: () => zkCloudWorkerClient }); module.exports = __toCommonJS(index_exports); // dist/node/cloud/utils/graphql.js var defaultToken = "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"; async function getBalanceFromGraphQL(params) { const { publicKey, mina } = params; const tokenId = params.tokenId ?? defaultToken; if (mina.length === 0) throw new Error("no mina endpoints provided"); const account = await fetchAccountInternal({ publicKey, tokenId, mina, queryType: "balance" }); const balance = account?.account?.balance?.total; return balance ? BigInt(balance) : 0n; } async function getAccountFromGraphQL(params) { const { publicKey, mina } = params; const tokenId = params.tokenId ?? defaultToken; if (mina.length === 0) throw new Error("no mina endpoints provided"); const account = await fetchAccountInternal({ publicKey, tokenId, mina, queryType: "account" }); return account?.account; } async function fetchAccountInternal(params) { const { publicKey, tokenId, mina, timeout, queryType } = params; const query = queryType === "balance" ? balanceQuery(publicKey, tokenId) : accountQuery(publicKey, tokenId); let [response, error] = await makeGraphqlRequest({ query, mina, timeout }); if (error !== void 0) return { account: void 0, error }; const account = response?.data?.account; if (!account) { return { account: void 0, error: { statusCode: 404, statusText: `fetchAccount: Account with public key ${publicKey} does not exist.` } }; } return { account, error: void 0 }; } async function makeGraphqlRequest(params) { const defaultTimeout = 5 * 60 * 1e3; const timeout = params.timeout ?? defaultTimeout; const { query, mina } = params; const graphqlEndpoint = mina[0]; const fallbackEndpoints = mina.slice(1); if (graphqlEndpoint === "none") throw Error("Should have made a graphql request, but don't know to which endpoint."); let timeouts = []; const clearTimeouts = () => { timeouts.forEach((t) => clearTimeout(t)); timeouts = []; }; const makeRequest = async (url) => { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeout); timeouts.push(timer); let body = JSON.stringify({ operationName: null, query, variables: {} }); try { let response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body, signal: controller.signal }); return checkResponseStatus(response); } finally { clearTimeouts(); } }; let timeoutErrors = []; let urls = [graphqlEndpoint, ...fallbackEndpoints]; for (let i = 0; i < urls.length; i += 2) { let url1 = urls[i]; let url2 = urls[i + 1]; if (url2 === void 0) { try { return await makeRequest(url1); } catch (error) { return [void 0, inferError(error)]; } } try { return await Promise.race([makeRequest(url1), makeRequest(url2)]); } catch (unknownError) { let error = inferError(unknownError); if (error.statusCode === 408) { timeoutErrors.push({ url1, url2, error }); } else { return [void 0, error]; } } } const statusText = timeoutErrors.map(({ url1, url2, error }) => `Request to ${url1} and ${url2} timed out. Error: ${error}`).join("\n"); return [void 0, { statusCode: 408, statusText }]; } function inferError(error) { let errorMessage = JSON.stringify(error); if (error instanceof AbortSignal) { return { statusCode: 408, statusText: `Request Timeout: ${errorMessage}` }; } else { return { statusCode: 500, statusText: `Unknown Error: ${errorMessage}` }; } } async function checkResponseStatus(response) { if (response.ok) { const jsonResponse = await response.json(); if (jsonResponse.errors && jsonResponse.errors.length > 0) { return [ void 0, { statusCode: response.status, statusText: jsonResponse.errors.map((error) => error.message).join("\n") } ]; } else if (jsonResponse.data === void 0) { return [ void 0, { statusCode: response.status, statusText: `GraphQL response data is undefined` } ]; } return [jsonResponse, void 0]; } else { return [ void 0, { statusCode: response.status, statusText: response.statusText } ]; } } var balanceQuery = (publicKey, tokenId) => `{ account(publicKey: "${publicKey}", token: "${tokenId}") { balance { total } } } `; var accountQuery = (publicKey, tokenId) => `{ account(publicKey: "${publicKey}", token: "${tokenId}") { publicKey token nonce balance { total } tokenSymbol receiptChainHash timing { initialMinimumBalance cliffTime cliffAmount vestingPeriod vestingIncrement } permissions { editState access send receive setDelegate setPermissions setVerificationKey { auth txnVersion } setZkappUri editActionState setTokenSymbol incrementNonce setVotingFor setTiming } delegateAccount { publicKey } votingFor zkappState verificationKey { verificationKey hash } actionState provedState zkappUri } } `; // dist/node/cloud/utils/utils.js function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function makeString(length) { let outString = ``; const inOptions = `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`; for (let i = 0; i < length; i++) { outString += inOptions.charAt(Math.floor(Math.random() * inOptions.length)); } return outString; } function formatTime(ms) { if (ms === void 0) return ""; if (ms < 1e3) return ms.toString() + " ms"; if (ms < 60 * 1e3) return parseInt((ms / 1e3).toString()).toString() + " sec"; if (ms < 60 * 60 * 1e3) { const minutes = parseInt((ms / 1e3 / 60).toString()); const seconds = parseInt(((ms - minutes * 60 * 1e3) / 1e3).toString()); return minutes.toString() + " min " + seconds.toString() + " sec"; } else { const hours = parseInt((ms / 1e3 / 60 / 60).toString()); const minutes = parseInt(((ms - hours * 60 * 60 * 1e3) / 1e3 / 60).toString()); return hours.toString() + " h " + minutes.toString() + " min"; } } var Memory = class _Memory { constructor() { _Memory.rss = 0; } // eslint-disable-next-line @typescript-eslint/no-inferrable-types static info(description = ``, fullInfo = false) { const memoryData = process.memoryUsage(); const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024)} MB`; const oldRSS = _Memory.rss; _Memory.rss = Math.round(memoryData.rss / 1024 / 1024); const memoryUsage = fullInfo ? { step: `${description}:`, rssDelta: `${(oldRSS === 0 ? 0 : _Memory.rss - oldRSS).toString()} MB -> Resident Set Size memory change`, rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated`, heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`, heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`, external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory` } : `RSS memory ${description}: ${formatMemoryUsage(memoryData.rss)}${oldRSS === 0 ? `` : `, changed by ` + (_Memory.rss - oldRSS).toString() + ` MB`}`; console.log(memoryUsage); } }; Memory.rss = 0; // dist/node/cloud/utils/base64.js var TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; function bigintToBase56(value) { const digits = toBase(value, 56n); const str = digits.map((x) => TABLE[Number(x)]).join(""); return str; } function bigintFromBase56(str) { const base56Digits = str.split("").map((x2) => BigInt(TABLE.indexOf(x2))); const x = fromBase(base56Digits, 56n); return x; } function bigintToBase64(value) { const digits = toBase(value, 64n); const str = digits.map((x) => TABLE[Number(x)]).join(""); return str; } function bigintFromBase64(str) { const base64Digits = str.split("").map((x2) => BigInt(TABLE.indexOf(x2))); const x = fromBase(base64Digits, 64n); return x; } function fromBase(digits, base) { if (base <= 0n) throw Error("fromBase: base must be positive"); let basePowers = []; for (let power = base, n = 1; n < digits.length; power **= 2n, n *= 2) { basePowers.push(power); } let k = basePowers.length; digits = digits.concat(Array(2 ** k - digits.length).fill(0n)); for (let i = 0; i < k; i++) { let newDigits = Array(digits.length >> 1); let basePower = basePowers[i]; for (let j = 0; j < newDigits.length; j++) { newDigits[j] = digits[2 * j] + basePower * digits[2 * j + 1]; } digits = newDigits; } console.assert(digits.length === 1); let [digit] = digits; return digit; } function toBase(x, base) { if (base <= 0n) throw Error("toBase: base must be positive"); let basePowers = []; for (let power = base; power <= x; power **= 2n) { basePowers.push(power); } let digits = [x]; let k = basePowers.length; for (let i = 0; i < k; i++) { let newDigits = Array(2 * digits.length); let basePower = basePowers[k - 1 - i]; for (let j = 0; j < digits.length; j++) { let x2 = digits[j]; let high = x2 / basePower; newDigits[2 * j + 1] = high; newDigits[2 * j] = x2 - high * basePower; } digits = newDigits; } while (digits[digits.length - 1] === 0n) { digits.pop(); } return digits; } // dist/node/cloud/worker/cloud.js var Cloud = class { /** * Constructor for the Cloud class * @param params the parameters for the Cloud class * @param params.id the id of the user * @param params.jobId the job id * @param params.stepId the step id * @param params.taskId the task id * @param params.cache the cache folder. Use it to get the Cache object: cache = Cache.FileSystem(this.cloud.cache); * @param params.developer the developer id * @param params.repo the repo id * @param params.task the task id * @param params.userId the user id * @param params.args the arguments, should be a string or serialized JSON * @param params.metadata the metadata, should be a string or serialized JSON * @param params.chain the blockchain network * @param params.isLocalCloud a boolean to check if the cloud is local or not */ constructor(params) { const { id, jobId, stepId, taskId, cache, developer, repo, task, userId, args, metadata, isLocalCloud, chain } = params; this.id = id; this.jobId = jobId; this.stepId = stepId; this.taskId = taskId; this.cache = cache; this.developer = developer; this.repo = repo; this.task = task; this.userId = userId; this.args = args; this.metadata = metadata; this.isLocalCloud = isLocalCloud ?? false; this.chain = chain; } }; // dist/node/cloud/worker/worker.js var zkCloudWorker = class { /** * Constructor for the zkCloudWorker class * @param cloud the cloud instance provided by the zkCloudWorker in the local environment or in the cloud */ constructor(cloud) { this.cloud = cloud; } // Those methods should be implemented for recursive proofs calculations /** * Creates a new proof from a transaction * @param transaction the transaction * @returns the serialized proof */ async create(transaction) { return void 0; } /** * Merges two proofs * @param proof1 the first proof * @param proof2 the second proof * @returns the merged proof */ async merge(proof1, proof2) { return void 0; } // Those methods should be implemented for anything except for recursive proofs /** * Executes the transactions * @param transactions the transactions, can be empty list * @returns the result */ async execute(transactions) { return void 0; } /* Process the transactions received by the cloud * @param transactions: the transactions */ async processTransactions(transactions) { } /** * process the task defined by the developer * @returns the result */ async task() { return void 0; } }; // dist/node/cloud/config.js var config = { MINAFEE: "200000000", ZKCLOUDWORKER_AUTH: "M6t4jtbBAFFXhLERHQWyEB9JA9xi4cWqmYduaCXtbrFjb7yaY7TyaXDunKDJNiUTBEcyUomNXJgC", ZKCLOUDWORKER_API: "https://api.zkcloudworker.com/v1/", ZKCLOUDWORKER_NATS: "https://cloud.zkcloudworker.com:4222", ZKCLOUDWORKER_NATS_WS: "wss://cloud.zkcloudworker.com:4223" }; var config_default = config; // dist/node/cloud/networks.js var Mainnet = { mina: [ //"https://proxy.devnet.minaexplorer.com/graphql", "https://api.minascan.io/node/mainnet/v1/graphql" ], archive: [ "https://api.minascan.io/archive/mainnet/v1/graphql" //"https://archive.devnet.minaexplorer.com", ], explorerAccountUrl: "https://minascan.io/mainnet/account/", explorerTransactionUrl: "https://minascan.io/mainnet/tx/", chainId: "mainnet", name: "Mainnet" }; var Local = { mina: [], archive: [], chainId: "local" }; var Devnet = { mina: [ "https://api.minascan.io/node/devnet/v1/graphql" //"https://proxy.devnet.minaexplorer.com/graphql", ], archive: [ "https://api.minascan.io/archive/devnet/v1/graphql" //"https://archive.devnet.minaexplorer.com", ], explorerAccountUrl: "https://minascan.io/devnet/account/", explorerTransactionUrl: "https://minascan.io/devnet/tx/", chainId: "devnet", name: "Devnet", faucet: "https://faucet.minaprotocol.com" }; var Zeko = { mina: ["https://devnet.zeko.io/graphql"], archive: ["https://devnet.zeko.io/graphql"], explorerAccountUrl: "https://zekoscan.io/devnet/account/", explorerTransactionUrl: "https://zekoscan.io/devnet/tx/", chainId: "zeko", name: "Zeko", faucet: "https://zeko.io/faucet" }; var Lightnet = { mina: ["http://localhost:8080/graphql"], archive: ["http://localhost:8282"], accountManager: "http://localhost:8181", chainId: "lightnet", name: "Lightnet" }; var networks = [Mainnet, Local, Devnet, Zeko, Lightnet]; // dist/node/mina/api/api.js var import_chalk = __toESM(require("chalk"), 1); // dist/node/mina/local/local.js var LocalCloud = class _LocalCloud extends Cloud { /** * Constructor for LocalCloud * @param params the parameters to create the LocalCloud * @param params.job the job data * @param params.chain the blockchain to execute the job on, can be any blockchain, not only local * @param params.cache the cache folder * @param params.stepId the step id * @param params.localWorker the worker to execute the tasks */ constructor(params) { const { job, chain, cache, stepId, localWorker } = params; const { id, jobId, developer, repo, task, userId, args, metadata, taskId } = job; super({ id, jobId, stepId: stepId ?? "stepId", taskId: taskId ?? "taskId", cache: cache ?? "./cache", developer, repo, task, userId, args, metadata, isLocalCloud: true, chain }); this.localWorker = localWorker; } /** * Provides the deployer key pair for testing and development * @returns the deployer key pair */ async getDeployer() { const privateKey = process.env.DEPLOYER_PRIVATE_KEY; const publicKey = process.env.DEPLOYER_PUBLIC_KEY; try { return privateKey === void 0 || publicKey === void 0 ? void 0 : { privateKey, publicKey }; } catch (error) { console.error(`getDeployer: error getting deployer key pair: ${error}`, error); return void 0; } } /** * Releases the deployer key pair */ async releaseDeployer(params) { console.log("LocalCloud: releaseDeployer", params); } /** * Gets the data by key * @param key the key to get the data * @returns the data */ async getDataByKey(key) { const value = LocalStorage.data[key]; return value; } /** * Saves the data by key * @param key the key to save the data * @param value the value to save */ async saveDataByKey(key, value) { if (value !== void 0) LocalStorage.data[key] = value; else delete LocalStorage.data[key]; } /** * Saves the file * @param filename the filename to save * @param value the value to save */ async saveFile(filename, value) { LocalStorage.files[filename] = value; } /** * Loads the file * @param filename * @returns the file data */ async loadFile(filename) { return LocalStorage.files[filename]; } /** * Encrypts the data * @param params * @param params.data the data * @param params.context the context * @param params.keyId the key id, optional * @returns encrypted data */ async encrypt(params) { return JSON.stringify(params); } /** * Decrypts the data * @param params * @param params.data the data * @param params.context the context * @param params.keyId the key id, optional * @returns */ async decrypt(params) { const { data, context, keyId } = JSON.parse(params.data); if (context !== params.context) { console.error("decrypt: context mismatch"); return void 0; } if (keyId !== params.keyId) { console.error("decrypt: keyId mismatch"); return void 0; } return data; } /** * Generates an id for local cloud * @returns generated unique id */ static generateId(tx = void 0) { return Date.now() + "." + makeString(32); } /** * Send transactions to the local cloud * @param transactions the transactions to add * @returns the transaction ids */ async sendTransactions(transactions) { return await _LocalCloud.addTransactions(transactions); } /** * Adds transactions to the local cloud * @param transactions the transactions to add * @returns the transaction ids */ static async addTransactions(transactions) { const timeReceived = Date.now(); const txs = []; transactions.forEach((tx) => { if (typeof tx === "string") { const txId = _LocalCloud.generateId(JSON.stringify({ tx, time: timeReceived })); const transaction = { txId, transaction: tx, timeReceived, status: "accepted" }; LocalStorage.transactions[txId] = transaction; txs.push(transaction); } else { LocalStorage.transactions[tx.txId] = tx; txs.push(tx); } }); return txs; } /** * Deletes a transaction from the local cloud * @param txId the transaction id to delete */ async deleteTransaction(txId) { if (LocalStorage.transactions[txId] === void 0) throw new Error(`deleteTransaction: Transaction ${txId} not found`); delete LocalStorage.transactions[txId]; } async getTransactions() { const txs = Object.keys(LocalStorage.transactions).map((txId) => { return LocalStorage.transactions[txId]; }); return txs; } /** * Publish the transaction metadata in human-readable format * @param params * @param params.txId the transaction id * @param params.metadata the metadata */ async publishTransactionMetadata(params) { console.log("publishTransactionMetadata:", params); } /** * Runs the worker in the local cloud * @param params the parameters to run the worker * @param params.command the command to run * @param params.data the data to use * @param params.chain the blockchain to execute the job on * @param params.localWorker the worker to execute the tasks * @returns the job id */ static async run(params) { const { command, data, chain, localWorker } = params; const { developer, repo, transactions, task, userId, args, metadata } = data; const timeCreated = Date.now(); const jobId = _LocalCloud.generateId(); const job = { id: "local", jobId, developer, repo, task, userId, args, metadata, txNumber: command === "recursiveProof" ? transactions.length : 1, timeCreated, timeStarted: timeCreated, chain }; const cloud = new _LocalCloud({ job, chain, localWorker }); const worker = await localWorker(cloud); if (worker === void 0) throw new Error("worker is undefined"); const result = command === "recursiveProof" ? await _LocalCloud.sequencer({ worker, data }) : command === "execute" ? await worker.execute(transactions) : void 0; const timeFinished = Date.now(); if (result !== void 0) { LocalStorage.jobEvents[jobId] = { jobId, jobStatus: "finished", eventTime: timeFinished, result }; job.timeFinished = timeFinished; job.jobStatus = "finished"; job.result = result; } else { LocalStorage.jobEvents[jobId] = { jobId, jobStatus: "failed", eventTime: timeFinished }; job.timeFailed = timeFinished; job.jobStatus = "failed"; } job.billedDuration = timeFinished - timeCreated; LocalStorage.jobs[jobId] = job; return jobId; } /** * Runs the recursive proof in the local cloud * @param data the data to use * @param data.transactions the transactions to process * @param data.task the task to execute * @param data.userId the user id * @param data.args the arguments for the job * @param data.metadata the metadata for the job * @returns the job id */ async recursiveProof(data) { return await _LocalCloud.run({ command: "recursiveProof", data: { developer: this.developer, repo: this.repo, transactions: data.transactions, task: data.task ?? "recursiveProof", userId: data.userId, args: data.args, metadata: data.metadata }, chain: this.chain, localWorker: this.localWorker }); } /** * Executes the task in the local cloud * @param data the data to use * @param data.transactions the transactions to process * @param data.task the task to execute * @param data.userId the user id * @param data.args the arguments for the job * @param data.metadata the metadata for the job * @returns the job id */ async execute(data) { return await _LocalCloud.run({ command: "execute", data: { developer: this.developer, repo: this.repo, transactions: data.transactions, task: data.task, userId: data.userId, args: data.args, metadata: data.metadata }, chain: this.chain, localWorker: this.localWorker }); } /** * Gets the job result * @param jobId the job id * @returns the job data */ async jobResult(jobId) { return LocalStorage.jobs[jobId]; } /** * Adds a task to the local cloud * @param data the data to use * @param data.task the task to execute * @param data.startTime the start time for the task * @param data.userId the user id * @param data.args the arguments for the job * @param data.metadata the metadata for the job * @returns the task id */ async addTask(data) { const taskId = _LocalCloud.generateId(); LocalStorage.tasks[taskId] = { ...data, id: "local", taskId, timeCreated: Date.now(), developer: this.developer, repo: this.repo, chain: this.chain }; return taskId; } /** * Deletes a task from the local cloud * @param taskId the task id to delete */ async deleteTask(taskId) { if (LocalStorage.tasks[taskId] === void 0) throw new Error(`deleteTask: Task ${taskId} not found`); delete LocalStorage.tasks[taskId]; } /** * Processes the tasks in the local cloud */ async processTasks() { await _LocalCloud.processLocalTasks({ developer: this.developer, repo: this.repo, localWorker: this.localWorker, chain: this.chain }); } /** * Processes the local tasks * @param params the parameters to process the local tasks * @param params.developer the developer of the repo * @param params.repo the repo * @param params.localWorker the worker to execute the tasks * @param params.chain the blockchain to execute the job on */ static async processLocalTasks(params) { const { developer, repo, localWorker, chain } = params; for (const taskId in LocalStorage.tasks) { const data = LocalStorage.tasks[taskId]; const jobId = _LocalCloud.generateId(); const timeCreated = Date.now(); if (data.startTime !== void 0 && data.startTime < timeCreated) continue; const job = { id: "local", jobId, taskId, developer, repo, task: data.task, userId: data.userId, args: data.args, metadata: data.metadata, txNumber: 1, timeCreated }; const cloud = new _LocalCloud({ job, chain, localWorker }); const worker = await localWorker(cloud); const result = await worker.task(); const timeFinished = Date.now(); if (result !== void 0) { LocalStorage.jobEvents[jobId] = { jobId, jobStatus: "finished", eventTime: timeFinished, result }; job.timeFinished = timeFinished; } else { LocalStorage.jobEvents[jobId] = { jobId, jobStatus: "failed", eventTime: timeFinished }; job.timeFailed = timeFinished; } job.billedDuration = timeFinished - timeCreated; LocalStorage.jobs[jobId] = job; } let count = 0; for (const task in LocalStorage.tasks) count++; return count; } /** * Runs the sequencer in the local cloud * @param params the parameters to run the sequencer * @param params.worker the worker to execute the tasks * @param params.data the data to use * @returns the proof */ static async sequencer(params) { const { worker, data } = params; const { transactions } = data; if (transactions.length === 0) throw new Error("No transactions to process"); const proofs = []; for (const transaction of transactions) { const result = await worker.create(transaction); if (result === void 0) throw new Error("Failed to create proof"); proofs.push(result); } let proof = proofs[0]; for (let i = 1; i < proofs.length; i++) { const result = await worker.merge(proof, proofs[i]); if (result === void 0) throw new Error("Failed to merge proofs"); proof = result; } return proof; } /** * forces the worker to restart */ async forceWorkerRestart() { throw new Error("forceWorkerRestart called in LocalCloud"); } }; var LocalStorage = class _LocalStorage { /** * Saves the data. * @param name The name to save the data under. * @throws Error Method not implemented to keep web compatibility. */ static async saveData(name) { throw new Error("Method not implemented to keep web compatibility."); const data = { jobs: _LocalStorage.jobs, data: _LocalStorage.data, transactions: _LocalStorage.transactions, tasks: _LocalStorage.tasks }; const filename = name + ".cloud"; } /** * Loads the data. * @param name The name to load the data from. * @throws Error Method not implemented to keep web compatibility. */ static async loadData(name) { throw new Error("Method not implemented to keep web compatibility."); const filename = name + ".cloud"; } }; LocalStorage.jobs = {}; LocalStorage.jobEvents = {}; LocalStorage.data = {}; LocalStorage.files = {}; LocalStorage.transactions = {}; LocalStorage.tasks = {}; // dist/node/mina/api/api.js var { ZKCLOUDWORKER_AUTH, ZKCLOUDWORKER_API } = config_default; var zkCloudWorkerClient = class { /** * Constructor for the API class * @param params the parameters for the API class * @param params.jwt The jwt token for authentication, get it at https://t.me/minanft_bot?start=auth * @param params.zkcloudworker The local worker for the serverless api to test the code locally * @param params.chain The blockchain network to use * @param params.webhook The webhook for the serverless api to get the results */ constructor(params) { const { jwt, zkcloudworker, webhook } = params; this.jwt = jwt; const chain = params.chain ?? "devnet"; this.chain = chain; this.endpoint = chain === "devnet" || chain === "zeko" || chain === "mainnet" ? ZKCLOUDWORKER_API + chain : void 0; this.webhook = webhook; if (jwt === "local") { if (zkcloudworker === void 0) throw new Error("worker is required for local mode"); this.localWorker = zkcloudworker; } } /** * Starts a new job for the proof calculation using serverless api call * @param data the data for the proof call * @param data.developer the developer * @param data.repo the repo to use * @param data.transactions the transactions * @param data.task the task of the job * @param data.userId the userId of the job * @param data.args the arguments of the job, should be serialized JSON or string * @param data.metadata the metadata of the job, should be serialized JSON or string * @param data.webhook the webhook for the job * @returns { success: boolean, error?: string, jobId?: string } * where jonId is the jobId of the job * * The developers repo should provide a zkcloudworker function * that can be called with the given parameters, see the examples */ async recursiveProof(data) { const result = await this.apiHub("recursiveProof", data); if (result.data === "error" || typeof result.data === "string" && result.data.startsWith("error")) return { success: false, error: result.error }; else if (result.success === false || result.data?.success === false) return { success: false, error: result.error ?? result.data?.error ?? "recursiveProof call failed" }; else if (result.success === true && result.data?.success === true && result.data?.jobId !== void 0) return { success: result.success, jobId: result.data.jobId, error: result.error }; else return { success: false, error: "recursiveProof call error" }; } /** * Starts a new job for the function call using serverless api call * @param data the data for the proof call * @param data.developer the developer * @param data.repo the repo to use * @param data.transactions the transactions * @param data.task the task of the job * @param data.userId the userId of the job * @param data.args the arguments of the job * @param data.metadata the metadata of the job * @param data.mode the mode of the job execution: "sync" will not create a job, it will execute the function synchronously within 30 seconds and with the memory limit of 256 MB * @returns { success: boolean, error?: string, jobId?: string, result?: any } * where jonId is the jobId of the job (for async calls), result is the result of the job (for sync calls) */ async execute(data) { const result = await this.apiHub("execute", data); if (result.data === "error" || typeof result.data === "string" && result.data.startsWith("error")) return { success: false, error: result.error }; else if (result.success === false || result.data?.success === false) return { success: false, error: result.error ?? result.data?.error ?? "execute call failed" }; else if (result.success === true && data.mode === "sync" && result.data !== void 0) return { success: result.success, jobId: void 0, result: result.data, error: result.error }; else if (result.success === true && data.mode !== "sync" && result.data?.success === true && result.data?.jobId !== void 0) return { success: result.success, jobId: result.data.jobId, result: void 0, error: result.error }; else return { success: false, error: "execute call error" }; } /** * Sends transactions to the blockchain using serverless api call * @param data the data for the proof call * @param data.developer the developer * @param data.repo the repo to use * @param data.transactions the transactions * @returns { success: boolean, error?: string, txId?: string[] } * where txId is the transaction id of the transaction, in the sequence of the input transactions */ async sendTransactions(data) { const result = await this.apiHub("sendTransactions", data); if (result.data === "error") return { success: false, error: result.error }; else return { success: result.success, txId: result.data, error: result.error }; } /** * Gets the result of the job using serverless api call * @param data the data for the jobResult call * @param data.jobId the jobId of the job * @param data.includeLogs include logs in the result, default is false * @returns { success: boolean, error?: string, result?: any } * where result is the result of the job * if the job is not finished yet, the result will be undefined * if the job failed, the result will be undefined and error will be set * if the job is finished, the result will be set and error will be undefined * if the job is not found, the result will be undefined and error will be set */ async jobResult(data) { const result = await this.apiHub("jobResult", data); if (this.isError(result.data)) return { success: false, error: result.error, result: result.data }; else return { success: result.success, error: result.error, result: result.success ? result.data : result.data }; } /** * Deploys the code to the cloud using serverless api call * @param data the data for the deploy call * @param data.repo the repo to use * @param data.developer the developer * @param data.packageManager the package manager to use * @returns { success: boolean, error?: string, jobId?: string} * where jobId is the jobId of the job */ async deploy(data) { const { repo, developer, packageManager } = data; const result = await this.apiHub("deploy", { developer, repo, args: packageManager }); if (result.data === "error" || typeof result.data === "string" && result.data.startsWith("error")) return { success: false, error: result.error }; else return { success: result.success && result.data?.success, jobId: result.data?.jobId, error: result.error }; } /** * Gets the billing report for the jobs sent using JWT * @returns { success: boolean, error?: string, result?: any } * where result is the billing report */ async queryBilling() { const result = await this.apiHub("queryBilling", {}); if (this.isError(result.data)) return { success: false, error: result.error, result: result.data }; else return { success: result.success, error: result.error, result: result.data }; } /** * Gets the remaining balance * @returns { success: boolean, error?: string, result?: any } * where result is the balance */ async getBalance() { const result = await this.apiHub("getBalance", {}); if (this.isError(result.data)) return { success: false, error: result.error, result: result.data }; else return { success: result.success, error: result.error, result: result.data }; } /** * Waits for the job to finish * @param data the data for the waitForJobResult call * @param data.jobId the jobId of the job * @param data.maxAttempts the maximum number of attempts, default is 360 (2 hours) * @param data.interval the interval between attempts, default is 20000 (20 seconds) * @param data.maxErrors the maximum number of network errors, default is 10 * @param data.printLogs print logs, default is true * @returns { success: boolean, error?: string, result?: any } * where result is the result of the job */ async waitForJobResult(data) { if (this.jwt === "local") return this.jobResult({ jobId: data.jobId }); const maxAttempts = data?.maxAttempts ?? 360; const interval = data?.interval ?? 1e4; const maxErrors = data?.maxErrors ?? 10; const errorDelay = 3e4; const printedLogs = []; const printLogs = data.printLogs ?? true; function print(logs) { logs.forEach((log) => { if (printedLogs.includes(log) === false) { printedLogs.push(log); if (printLogs) { const text = log.replace(/error/gi, (matched) => import_chalk.default.red(matched)); console.log(text); } } }); } let attempts = 0; let errors = 0; while (attempts < maxAttempts) { const result = await this.apiHub("jobResult", { jobId: data.jobId, includeLogs: printLogs }); const isAllLogsFetched = result?.data?.isFullLog === true || printLogs === false; if (printLogs === true && result?.data?.logs !== void 0 && result?.data?.logs !== null && Array.isArray(result.data.logs) === true) print(result.data.logs); if (result.success === false) { errors++; if (errors > maxErrors) { return { success: false, error: "Too many network errors", result: void 0 }; } await sleep(errorDelay * errors); } else { if (this.isError(result.data) && isAllLogsFetched) return { success: false, error: result.error, result: result.data }; else if (result.data?.result !== void 0 && isAllLogsFetched) { return { success: result.success, error: result.error, result: result.data }; } else if (result.data?.jobStatus === "failed" && isAllLogsFetched) { return { success: false, error: "Job failed", result: result.data }; } await sleep(interval); } attempts++; } return { success: false, error: "Timeout", result: void 0 }; } /** * Calls the serverless API * @param command the command of the API * @param data the data of the API * */ async apiHub(command, data) { if (this.jwt === "local") { if (this.localWorker === void 0) throw new Error("localWorker is undefined"); switch (command) { case "recursiveProof": { const jobId = await LocalCloud.run({ command: "recursiveProof", data, chain: this.chain, localWorker: this.localWorker }); return { success: true, data: { success: true, jobId } }; } case "execute": { const jobId = await LocalCloud.run({ command: "execute", data, chain: this.chain, localWorker: this.localWorker }); if (data.mode === "sync") return { success: true, data: LocalStorage.jobEvents[jobId].result }; else return { success: true, data: { success: true, jobId } }; } case "jobResult": { const job = LocalStorage.jobs[data.jobId]; if (job === void 0) { return { success: false, error: "local job not found" }; } else { return { success: true, data: job }; } } case "sendTransactions": { return { success: true, data: await LocalCloud.addTransactions(data.transactions) }; } case "deploy": return { success: true, data: "local_deploy" }; case "queryBilling": return { success: true, data: "local_queryBilling" }; default: return { success: false, error: "local_error" }; } } else { if (this.endpoint === void 0) throw new Error("zkCloudWorker supports only mainnet, devnet and zeko chains in the cloud."); const apiData = { auth: ZKCLOUDWORKER_AUTH, command, jwtToken: this.jwt, data, chain: this.chain, webhook: this.webhook // TODO: implement webhook code on AWS }; try { const response = await fetch(this.endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(apiData) }); if (!response.ok) { return { success: false, error: `fetch error: ${response.statusText} status: ${response.status}` }; } const data2 = await response.json(); return { success: true, data: data2 }; } catch (error) { console.error("apiHub error:", error.message ?? error); return { success: false, error }; } } } // eslint-disable-next-line @typescript-eslint/no-explicit-any isError(data) { if (data === "error") return true; if (data?.jobStatus === "failed") return true; if (typeof data === "string" && data.toLowerCase().startsWith("error")) return true; if (data !== void 0 && data.error !== void 0) return true; return false; } }; // dist/node/mina/utils/base64.js var import_o1js = require("o1js"); var TABLE2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; function fieldToBase56(field) { const digits = toBase(field.toBigInt(), 56n); const str = digits.map((x) => TABLE2[Number(x)]).join(""); return str; } function fieldFromBase56(str) { const base56Digits = str.split("").map((x2) => BigInt(TABLE2.indexOf(x2))); const x = fromBase(base56Digits, 56n); return (0, import_o1js.Field)(x); } function fieldToBase64(field) { const digits = toBase(field.toBigInt(), 64n); const str = digits.map((x) => TABLE2[Number(x)]).join(""); return str; } function fieldFromBase64(str) { const base64Digits = str.split("").map((x2) => BigInt(TABLE2.indexOf(x2))); const x = fromBase(base64Digits, 64n); return (0, import_o1js.Field)(x); } // dist/node/mina/utils/fetch.js var import_o1js2 = require("o1js"); async function fetchMinaAccount(params) { const { publicKey, tokenId, force = false } = params; const timeout = 1e3 * 60 * 3; let attempt = 0; const startTime = Date.now(); let result = { account: void 0 }; while (Date.now() - startTime < timeout) { try { const result2 = await (0, import_o1js2.fetchAccount)({ publicKey, tokenId }, void 0, { timeout: 5 * 1e3 }); return result2; } catch (error) { if (force === true) console.log("Error in fetchMinaAccount:", { error, publicKey: typeof publicKey === "string" ? publicKey : publicKey.toBase58(), tokenId: tokenId?.toString(), force }); else { console.log("fetchMinaAccount error", { error, publicKey: typeof publicKey === "string" ? publicKey : publicKey.toBase58(), tokenId: tokenId?.toString(), force }); return result; } } attempt++; await sleep(1e3 * 6 * attempt); } if (force === true) throw new Error(`fetchMinaAccount timeout ${{ publicKey: typeof publicKey === "string" ? publicKey : publicKey.toBase58(), tokenId: tokenId?.toString(), force }}`); else console.log("fetchMinaAccount timeout", typeof publicKey === "string" ? publicKey : publicKey.toBase58(), tokenId?.toString(), force); return result; } async function fetchMinaActions(publicKey, fromActionState, endActionState) { const timeout = 1e3 * 60 * 600; const startTime = Date.now(); while (Date.now() - startTime < timeout) { try { let actions = await import_o1js2.Mina.fetchActions(publicKey, { fromActionState, endActionState }); if (Array.isArray(actions)) return actions; else console.log("Cannot fetch actions - wrong format"); } catch (error) { console.log("Error in fetchMinaActions", error.toString().substring(0, 300)); } await sleep(1e3 * 60 * 2); } console.log("Timeout in fetchMinaActions"); return void 0; } async function checkMinaZkappTransaction(hash) { try { const result = await (0, import_o1js2.checkZkappTransaction)(hash); return result; } catch (error) { console.error("Error in checkZkappTransaction:", error); return { success: fals