diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b20323219..532949107 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-05-14" +channel = "nightly-2024-07-21" components = [ "rust-std", "rust-src" ] targets = [ "wasm32-unknown-unknown" ] diff --git a/sdk/package.json b/sdk/package.json index 744d84158..f1ef4ca25 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -7,12 +7,20 @@ ], "license": "GPL-3.0", "type": "module", - "main": "./dist/node.js", - "browser": "./dist/index.js", + "main": "./dist/testnet/node.js", + "browser": "./dist/testnet/browser.js", "exports": { ".": { - "node": "./dist/node.js", - "default": "./dist/index.js" + "node": "./dist/testnet/node.js", + "default": "./dist/testnet/browser.js" + }, + "./testnet.js": { + "node": "./dist/testnet/node.js", + "default": "./dist/testnet/browser.js" + }, + "./mainnet.js": { + "node": "./dist/mainnet/node.js", + "default": "./dist/mainnet/browser.js" } }, "files": [ diff --git a/sdk/rollup.config.js b/sdk/rollup.config.js index c4238869a..d761599c2 100644 --- a/sdk/rollup.config.js +++ b/sdk/rollup.config.js @@ -4,10 +4,10 @@ import $package from "./package.json" assert { type: "json" }; export default { input: { - index: "./src/index.ts", - worker: "./src/worker.ts", - node: "./src/node.ts", - "node-polyfill": "./src/node-polyfill.ts", + "testnet/browser": "./src/testnet/browser.ts", + "testnet/worker": "./src/shared/worker.ts", + "testnet/node": "./src/testnet/node.ts", + "node-polyfill": "./src/shared/node-polyfill.ts", }, output: { dir: `dist`, diff --git a/sdk/src/node.ts b/sdk/src/node.ts deleted file mode 100644 index 6aadbc294..000000000 --- a/sdk/src/node.ts +++ /dev/null @@ -1,2 +0,0 @@ -import "./node-polyfill"; -export * from "./index"; diff --git a/sdk/src/account.ts b/sdk/src/shared/account.ts similarity index 100% rename from sdk/src/account.ts rename to sdk/src/shared/account.ts diff --git a/sdk/src/function-key-provider.ts b/sdk/src/shared/function-key-provider.ts similarity index 100% rename from sdk/src/function-key-provider.ts rename to sdk/src/shared/function-key-provider.ts diff --git a/sdk/src/index.ts b/sdk/src/shared/index.ts similarity index 97% rename from sdk/src/index.ts rename to sdk/src/shared/index.ts index a7c6e6cb1..05f119a26 100644 --- a/sdk/src/index.ts +++ b/sdk/src/shared/index.ts @@ -1,4 +1,4 @@ -import {VerifyingKey, Metadata} from "@provablehq/wasm"; +import {VerifyingKey, Metadata} from "@provablehq/wasm/testnet.js"; const KEY_STORE = Metadata.baseUrl(); @@ -158,7 +158,7 @@ export { ViewKey, initThreadPool, verifyFunctionExecution, -} from "@provablehq/wasm"; +} from "@provablehq/wasm/testnet.js"; export { initializeWasm }; diff --git a/sdk/src/managed-worker.ts b/sdk/src/shared/managed-worker.ts similarity index 100% rename from sdk/src/managed-worker.ts rename to sdk/src/shared/managed-worker.ts diff --git a/sdk/src/models/block.ts b/sdk/src/shared/models/block.ts similarity index 100% rename from sdk/src/models/block.ts rename to sdk/src/shared/models/block.ts diff --git a/sdk/src/models/confirmed_transaction.ts b/sdk/src/shared/models/confirmed_transaction.ts similarity index 100% rename from sdk/src/models/confirmed_transaction.ts rename to sdk/src/shared/models/confirmed_transaction.ts diff --git a/sdk/src/models/execution.ts b/sdk/src/shared/models/execution.ts similarity index 100% rename from sdk/src/models/execution.ts rename to sdk/src/shared/models/execution.ts diff --git a/sdk/src/models/input.ts b/sdk/src/shared/models/input.ts similarity index 100% rename from sdk/src/models/input.ts rename to sdk/src/shared/models/input.ts diff --git a/sdk/src/models/output.ts b/sdk/src/shared/models/output.ts similarity index 100% rename from sdk/src/models/output.ts rename to sdk/src/shared/models/output.ts diff --git a/sdk/src/models/transactionModel.ts b/sdk/src/shared/models/transactionModel.ts similarity index 100% rename from sdk/src/models/transactionModel.ts rename to sdk/src/shared/models/transactionModel.ts diff --git a/sdk/src/models/transition.ts b/sdk/src/shared/models/transition.ts similarity index 100% rename from sdk/src/models/transition.ts rename to sdk/src/shared/models/transition.ts diff --git a/sdk/src/network-client.ts b/sdk/src/shared/network-client.ts similarity index 100% rename from sdk/src/network-client.ts rename to sdk/src/shared/network-client.ts diff --git a/sdk/src/node-polyfill.ts b/sdk/src/shared/node-polyfill.ts similarity index 100% rename from sdk/src/node-polyfill.ts rename to sdk/src/shared/node-polyfill.ts diff --git a/sdk/src/offline-key-provider.ts b/sdk/src/shared/offline-key-provider.ts similarity index 100% rename from sdk/src/offline-key-provider.ts rename to sdk/src/shared/offline-key-provider.ts diff --git a/sdk/src/polyfill/crypto.ts b/sdk/src/shared/polyfill/crypto.ts similarity index 100% rename from sdk/src/polyfill/crypto.ts rename to sdk/src/shared/polyfill/crypto.ts diff --git a/sdk/src/polyfill/fetch.ts b/sdk/src/shared/polyfill/fetch.ts similarity index 100% rename from sdk/src/polyfill/fetch.ts rename to sdk/src/shared/polyfill/fetch.ts diff --git a/sdk/src/polyfill/worker.ts b/sdk/src/shared/polyfill/worker.ts similarity index 100% rename from sdk/src/polyfill/worker.ts rename to sdk/src/shared/polyfill/worker.ts diff --git a/sdk/src/polyfill/xmlhttprequest.ts b/sdk/src/shared/polyfill/xmlhttprequest.ts similarity index 100% rename from sdk/src/polyfill/xmlhttprequest.ts rename to sdk/src/shared/polyfill/xmlhttprequest.ts diff --git a/sdk/src/program-manager.ts b/sdk/src/shared/program-manager.ts similarity index 99% rename from sdk/src/program-manager.ts rename to sdk/src/shared/program-manager.ts index 85aa2ba89..73c0b7bd8 100644 --- a/sdk/src/program-manager.ts +++ b/sdk/src/shared/program-manager.ts @@ -21,7 +21,7 @@ import { logAndThrow, ProgramManagerBase as WasmProgramManager, verifyFunctionExecution, AleoKeyProviderParams, CREDITS_PROGRAM_KEYS, } from "./index"; -import {Execution} from "@provablehq/wasm/dist/crates/aleo_wasm"; +import {Execution} from "@provablehq/wasm/testnet.js"; /** * Represents the options for executing a transaction in the Aleo network. @@ -78,7 +78,7 @@ class ProgramManager { constructor(host?: string | undefined, keyProvider?: FunctionKeyProvider | undefined, recordProvider?: RecordProvider | undefined) { this.host = host ? host : 'https://api.explorer.aleo.org/v1'; this.networkClient = new AleoNetworkClient(this.host); - + this.keyProvider = keyProvider ? keyProvider : new AleoKeyProvider(); this.recordProvider = recordProvider; } diff --git a/sdk/src/record-provider.ts b/sdk/src/shared/record-provider.ts similarity index 100% rename from sdk/src/record-provider.ts rename to sdk/src/shared/record-provider.ts diff --git a/sdk/src/utils.ts b/sdk/src/shared/utils.ts similarity index 100% rename from sdk/src/utils.ts rename to sdk/src/shared/utils.ts diff --git a/sdk/src/shared/worker.ts b/sdk/src/shared/worker.ts new file mode 100644 index 000000000..5846ec6e4 --- /dev/null +++ b/sdk/src/shared/worker.ts @@ -0,0 +1,146 @@ +import type { FunctionKeyPair } from "./function-key-provider"; +import { ProgramManager } from "./program-manager"; +import { AleoKeyProvider, AleoKeyProviderParams} from "./function-key-provider"; +import { expose } from "comlink"; + + +export interface WorkerAPI { + executeOffline: ( + localProgram: string, + aleoFunction: string, + inputs: string[], + privateKey: string + ) => Promise<{ outputs: any; execution: string } | string>; + + getPrivateKey: () => Promise; +} + + +export abstract class WorkerImpl { + abstract from_string(key: string): any; + + abstract to_string(): string; + + abstract verifyFunctionExecution(execution: any, verifying_key: any, program: any, function_id: string): boolean; + + init() { + const defaultHost = "https://api.explorer.aleo.org/v1"; + const keyProvider = new AleoKeyProvider(); + const programManager = new ProgramManager( + defaultHost, + keyProvider, + undefined + ); + + keyProvider.useCache(true); + + let lastLocalProgram: string = ""; + + const executeOffline = async ( + localProgram: string, + aleoFunction: string, + inputs: string[], + privateKey: string, + proveExecution = false + ) => { + console.log("Web worker: Executing function locally..."); + const startTime = performance.now(); + + try { + // Ensure the program is valid and that it contains the function specified + const program = programManager.createProgramFromSource(localProgram); + if (program instanceof Error) { + throw "Error creating program from source"; + } + const program_id = program.id(); + if (!program.hasFunction(aleoFunction)) { + throw `Program ${program_id} does not contain function ${aleoFunction}`; + } + const cacheKey = `${program_id}:${aleoFunction}`; + + // Get the program imports + const imports = await programManager.networkClient.getProgramImports( + localProgram + ); + + if (imports instanceof Error) { + throw "Error getting program imports"; + } + // Get the proving and verifying keys for the function + if (lastLocalProgram !== localProgram) { + const keys = await programManager.synthesizeKeys( + localProgram, + aleoFunction, + inputs, + this.from_string(privateKey) + ); + programManager.keyProvider.cacheKeys(cacheKey, keys); + lastLocalProgram = localProgram; + } + + // Pass the cache key to the execute function + const keyParams = new AleoKeyProviderParams({ + cacheKey: cacheKey, + }); + + // Execute the function locally + const response = await programManager.run( + localProgram, + aleoFunction, + inputs, + proveExecution, + imports, + keyParams, + undefined, + undefined, + this.from_string(privateKey), + ); + + // Return the outputs to the main thread + console.log( + `Web worker: Local execution completed in ${ + performance.now() - startTime + } ms` + ); + const outputs = response.getOutputs(); + const execution = response.getExecution(); + let executionString = ""; + + const keys = keyProvider.getKeys(cacheKey); + + if (keys instanceof Error) { + throw "Could not get verifying key"; + } + + const verifyingKey = keys[1]; + + if (execution) { + this.verifyFunctionExecution( + execution, + verifyingKey, + program, + "hello" + ); + executionString = execution.toString(); + console.log("Execution verified successfully: " + execution); + } else { + executionString = ""; + } + + console.log(`Function execution response: ${outputs}`); + + return { outputs: outputs, execution: executionString }; + } catch (error) { + console.error(error); + return error ? error.toString() : "Unknown error"; + } + } + + const getPrivateKey = async () => { + return this.to_string(); + }; + + const workerAPI = { executeOffline, getPrivateKey }; + expose(workerAPI); + } +} diff --git a/sdk/src/testnet/browser.ts b/sdk/src/testnet/browser.ts new file mode 100644 index 000000000..8c743d299 --- /dev/null +++ b/sdk/src/testnet/browser.ts @@ -0,0 +1,198 @@ +import {VerifyingKey, Metadata} from "@provablehq/wasm/testnet.js"; + +const KEY_STORE = Metadata.baseUrl(); + +interface Key { + locator: string, + prover: string, + verifier: string, + verifyingKey: () => VerifyingKey, +} + +function convert(metadata: Metadata): Key { + // This looks up the method name in VerifyingKey + const verifyingKey = (VerifyingKey as any)[metadata.verifyingKey]; + + if (!verifyingKey) { + throw new Error("Invalid method name: " + metadata.verifyingKey); + } + + return { + locator: metadata.locator, + prover: metadata.prover, + verifier: metadata.verifier, + verifyingKey, + }; +} + +const CREDITS_PROGRAM_KEYS = { + bond_public: convert(Metadata.bond_public()), + bond_validator: convert(Metadata.bond_validator()), + claim_unbond_public: convert(Metadata.claim_unbond_public()), + fee_private: convert(Metadata.fee_private()), + fee_public: convert(Metadata.fee_public()), + inclusion: convert(Metadata.inclusion()), + join: convert(Metadata.join()), + set_validator_state: convert(Metadata.set_validator_state()), + split: convert(Metadata.split()), + transfer_private: convert(Metadata.transfer_private()), + transfer_private_to_public: convert(Metadata.transfer_private_to_public()), + transfer_public: convert(Metadata.transfer_public()), + transfer_public_as_signer: convert(Metadata.transfer_public_as_signer()), + transfer_public_to_private: convert(Metadata.transfer_public_to_private()), + unbond_public: convert(Metadata.unbond_public()), +}; + +const PRIVATE_TRANSFER_TYPES = new Set([ + "transfer_private", + "private", + "transferPrivate", + "transfer_private_to_public", + "privateToPublic", + "transferPrivateToPublic", +]); +const VALID_TRANSFER_TYPES = new Set([ + "transfer_private", + "private", + "transferPrivate", + "transfer_private_to_public", + "privateToPublic", + "transferPrivateToPublic", + "transfer_public", + "transfer_public_as_signer", + "public", + "public_as_signer", + "transferPublic", + "transferPublicAsSigner", + "transfer_public_to_private", + "publicToPrivate", + "publicAsSigner", + "transferPublicToPrivate", +]); +const PRIVATE_TRANSFER = new Set([ + "private", + "transfer_private", + "transferPrivate", +]); +const PRIVATE_TO_PUBLIC_TRANSFER = new Set([ + "private_to_public", + "privateToPublic", + "transfer_private_to_public", + "transferPrivateToPublic", +]); +const PUBLIC_TRANSFER = new Set([ + "public", + "transfer_public", + "transferPublic", +]); +const PUBLIC_TRANSFER_AS_SIGNER = new Set([ + "public_as_signer", + "transfer_public_as_signer", + "transferPublicAsSigner", +]); +const PUBLIC_TO_PRIVATE_TRANSFER = new Set([ + "public_to_private", + "publicToPrivate", + "transfer_public_to_private", + "transferPublicToPrivate", +]); + +function logAndThrow(message: string): Error { + console.error(message); + throw message; +} + +import { Account } from "../shared/account"; +import { AleoNetworkClient, ProgramImports } from "../shared/network-client"; +import { Block } from "../shared/models/block"; +import { Execution } from "../shared/models/execution"; +import { Input } from "../shared/models/input"; +import { Output } from "../shared/models/output"; +import { TransactionModel } from "../shared/models/transactionModel"; +import { Transition } from "../shared/models/transition"; +import { + AleoKeyProvider, + AleoKeyProviderParams, + AleoKeyProviderInitParams, + CachedKeyPair, + FunctionKeyPair, + FunctionKeyProvider, + KeySearchParams, +} from "../shared/function-key-provider"; +import { + OfflineKeyProvider, + OfflineSearchParams +} from "../shared/offline-key-provider"; +import { + BlockHeightSearch, + NetworkRecordProvider, + RecordProvider, + RecordSearchParams, +} from "../shared/record-provider"; + +// @TODO: This function is no longer needed, remove it. +async function initializeWasm() { + console.warn("initializeWasm is deprecated, you no longer need to use it"); +} + +export { createAleoWorker } from "../shared/managed-worker"; + +export { ProgramManager } from "../shared/program-manager"; + +export { + Address, + Execution as FunctionExecution, + ExecutionResponse, + Field, + OfflineQuery, + PrivateKey, + PrivateKeyCiphertext, + Program, + ProgramManager as ProgramManagerBase, + ProvingKey, + RecordCiphertext, + RecordPlaintext, + Signature, + Transaction, + VerifyingKey, + ViewKey, + initThreadPool, + verifyFunctionExecution, +} from "@provablehq/wasm/testnet.js"; + +export { initializeWasm }; + +export { + Account, + AleoKeyProvider, + AleoKeyProviderParams, + AleoKeyProviderInitParams, + AleoNetworkClient, + Block, + BlockHeightSearch, + CachedKeyPair, + Execution, + FunctionKeyPair, + FunctionKeyProvider, + Input, + KeySearchParams, + NetworkRecordProvider, + ProgramImports, + OfflineKeyProvider, + OfflineSearchParams, + Output, + RecordProvider, + RecordSearchParams, + TransactionModel, + Transition, + CREDITS_PROGRAM_KEYS, + KEY_STORE, + PRIVATE_TRANSFER, + PRIVATE_TO_PUBLIC_TRANSFER, + PRIVATE_TRANSFER_TYPES, + PUBLIC_TRANSFER, + PUBLIC_TRANSFER_AS_SIGNER, + PUBLIC_TO_PRIVATE_TRANSFER, + VALID_TRANSFER_TYPES, + logAndThrow, +}; diff --git a/sdk/src/testnet/node.ts b/sdk/src/testnet/node.ts new file mode 100644 index 000000000..0886f7cea --- /dev/null +++ b/sdk/src/testnet/node.ts @@ -0,0 +1,2 @@ +import "../shared/node-polyfill"; +export * from "./browser"; diff --git a/sdk/src/testnet/worker.ts b/sdk/src/testnet/worker.ts new file mode 100644 index 000000000..e8373a8a8 --- /dev/null +++ b/sdk/src/testnet/worker.ts @@ -0,0 +1,21 @@ +import { initThreadPool, PrivateKey, verifyFunctionExecution } from "./browser"; +import { WorkerImpl } from "../shared/worker"; + +class TestnetWorker extends WorkerImpl { + from_string(key: string): any { + return PrivateKey.from_string(key); + } + + to_string(): string { + const privateKey = new PrivateKey(); + return privateKey.to_string(); + } + + verifyFunctionExecution(execution: any, verifying_key: any, program: any, function_id: string): boolean { + return verifyFunctionExecution(execution, verifying_key, program, function_id); + } +} + +await initThreadPool(); + +new TestnetWorker().init(); diff --git a/sdk/src/worker.ts b/sdk/src/worker.ts deleted file mode 100644 index 8611538c7..000000000 --- a/sdk/src/worker.ts +++ /dev/null @@ -1,135 +0,0 @@ -import {initThreadPool, ProgramManager, PrivateKey, verifyFunctionExecution, FunctionKeyPair} from "./index"; -import { AleoKeyProvider, AleoKeyProviderParams} from "./function-key-provider"; -import { expose } from "comlink"; - -await initThreadPool(); - -const defaultHost = "https://api.explorer.aleo.org/v1"; -const keyProvider = new AleoKeyProvider(); -const programManager = new ProgramManager( - defaultHost, - keyProvider, - undefined -); - -keyProvider.useCache(true); - -let lastLocalProgram: string = ""; - -export interface WorkerAPI { - executeOffline: ( - localProgram: string, - aleoFunction: string, - inputs: string[], - privateKey: string - ) => Promise<{ outputs: any; execution: string } | string>; - - getPrivateKey: () => Promise; -} -async function executeOffline( - localProgram: string, - aleoFunction: string, - inputs: string[], - privateKey: string, - proveExecution = false -) { - console.log("Web worker: Executing function locally..."); - const startTime = performance.now(); - - try { - // Ensure the program is valid and that it contains the function specified - const program = programManager.createProgramFromSource(localProgram); - if (program instanceof Error) { - throw "Error creating program from source"; - } - const program_id = program.id(); - if (!program.hasFunction(aleoFunction)) { - throw `Program ${program_id} does not contain function ${aleoFunction}`; - } - const cacheKey = `${program_id}:${aleoFunction}`; - - // Get the program imports - const imports = await programManager.networkClient.getProgramImports( - localProgram - ); - - if (imports instanceof Error) { - throw "Error getting program imports"; - } - // Get the proving and verifying keys for the function - if (lastLocalProgram !== localProgram) { - const keys = await programManager.synthesizeKeys( - localProgram, - aleoFunction, - inputs, - PrivateKey.from_string(privateKey) - ); - programManager.keyProvider.cacheKeys(cacheKey, keys); - lastLocalProgram = localProgram; - } - - // Pass the cache key to the execute function - const keyParams = new AleoKeyProviderParams({ - cacheKey: cacheKey, - }); - - // Execute the function locally - const response = await programManager.run( - localProgram, - aleoFunction, - inputs, - proveExecution, - imports, - keyParams, - undefined, - undefined, - PrivateKey.from_string(privateKey), - ); - - // Return the outputs to the main thread - console.log( - `Web worker: Local execution completed in ${ - performance.now() - startTime - } ms` - ); - const outputs = response.getOutputs(); - const execution = response.getExecution(); - let executionString = ""; - - const keys = keyProvider.getKeys(cacheKey); - - if (keys instanceof Error) { - throw "Could not get verifying key"; - } - - const verifyingKey = keys[1]; - - if (execution) { - verifyFunctionExecution( - execution, - verifyingKey, - program, - "hello" - ); - executionString = execution.toString(); - console.log("Execution verified successfully: " + execution); - } else { - executionString = ""; - } - - console.log(`Function execution response: ${outputs}`); - - return { outputs: outputs, execution: executionString }; - } catch (error) { - console.error(error); - return error ? error.toString() : "Unknown error"; - } -} - -async function getPrivateKey() { - const privateKey = new PrivateKey(); - return privateKey.to_string(); -} - -const workerAPI = { executeOffline, getPrivateKey }; -expose(workerAPI); diff --git a/sdk/tsconfig.json b/sdk/tsconfig.json index de3592536..b933e4d3d 100644 --- a/sdk/tsconfig.json +++ b/sdk/tsconfig.json @@ -17,7 +17,7 @@ // stricter type-checking for stronger correctness. Recommended by TS "strict": true, // use Node's module resolution algorithm, instead of the legacy TS one - "moduleResolution": "node", + "moduleResolution": "bundler", // interop between ESM and CJS modules. Recommended by TS "esModuleInterop": true, // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS