UNPKG

@silvana-one/mina-prover

Version:
528 lines 17.4 kB
import { Cloud, } from "@silvana-one/prover"; import { makeString } from "@silvana-one/mina-utils"; /** * LocalCloud is a cloud that runs on the local machine for testing and development * It uses LocalStorage to store jobs, tasks, transactions, and data * It uses a localWorker to execute the tasks * It can be used to test the cloud functionality without deploying to the cloud * @param localWorker the worker to execute the tasks */ export 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: id, jobId: jobId, stepId: stepId ?? "stepId", taskId: taskId ?? "taskId", cache: cache ?? "./cache", developer: developer, repo: repo, task: task, userId: userId, args: args, metadata: 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 === undefined || publicKey === undefined ? undefined : { privateKey, publicKey, }; } catch (error) { console.error(`getDeployer: error getting deployer key pair: ${error}`, error); return undefined; } } /** * 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 !== undefined) 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; //throw new Error("Method not implemented."); //await saveBinaryFile({ data: value, filename }); } /** * Loads the file * @param filename * @returns the file data */ async loadFile(filename) { return LocalStorage.files[filename]; //throw new Error("Method not implemented."); //const data = await loadBinaryFile(filename); //return data; } /** * 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 undefined; } if (keyId !== params.keyId) { console.error("decrypt: keyId mismatch"); return undefined; } return data; } /** * Generates an id for local cloud * @returns generated unique id */ static generateId(tx = undefined) { //const data = // tx ?? JSON.stringify({ time: Date.now(), data: makeString(32) }); //return stringHash(data); 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] === undefined) 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 === undefined) throw new Error("worker is undefined"); const result = command === "recursiveProof" ? await LocalCloud.sequencer({ worker, data, }) : command === "execute" ? await worker.execute(transactions) : undefined; const timeFinished = Date.now(); if (result !== undefined) { 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] === undefined) 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 !== undefined && data.startTime < timeCreated) continue; const job = { id: "local", jobId: jobId, taskId: taskId, developer, repo, task: data.task, userId: data.userId, args: data.args, metadata: data.metadata, txNumber: 1, timeCreated: timeCreated, }; const cloud = new LocalCloud({ job, chain, localWorker, }); const worker = await localWorker(cloud); const result = await worker.task(); const timeFinished = Date.now(); if (result !== undefined) { 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 === undefined) 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 === undefined) 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"); } } /** * LocalStorage is a local storage for the local cloud. * It stores jobs, tasks, transactions, and data. * It can be used to test the cloud functionality without deploying to the cloud. */ export class LocalStorage { /** The jobs */ static { this.jobs = {}; } /** The job events */ static { this.jobEvents = {}; } /** The data */ static { this.data = {}; } /** The files */ static { this.files = {}; } /** The transactions */ static { this.transactions = {}; } /** The tasks */ static { this.tasks = {}; } /** * 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"; // await saveFile({ data, filename }); } /** * 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"; /* const data = await loadFile(filename); if (data === undefined) return; LocalStorage.jobs = data.jobs; LocalStorage.data = data.data; LocalStorage.transactions = data.transactions; LocalStorage.tasks = data.tasks; */ } } //# sourceMappingURL=local.js.map