zkcloudworker
Version:
zkCloudWorker library
1,601 lines (1,588 loc) • 92.2 kB
JavaScript
"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