diff --git a/create-leo-app/template-devnode-js/README.md b/create-leo-app/template-devnode-js/README.md new file mode 100644 index 000000000..f6d94bc5d --- /dev/null +++ b/create-leo-app/template-devnode-js/README.md @@ -0,0 +1,34 @@ +# Aleo Local Development Server + Node.js + +## Initializing a Leo Devnode server +Note: Prior to the release of Leo 4.4.0, the Leo CLI should be installed from the following commit in order to use the Devnode tool: [5baf94e](https://github.com/ProvableHQ/leo/pull/28982/commits/5baf94e491189b89dca7c981c2b79dfc6af1d108) + +The Leo Devnode server is designed to enable developers to rapidly iterate on their Aleo program design. Deployment transactions do not require key synthesis or certificate generation and execution transitions do not require proofs. + +To initialize a Devnode server, run the following command: +```bash +leo devnode start --private-key APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH +``` +with optional `--verbosity` feature flag with settings `0, 1, and 2`. + +There is an additional command which lets users "fast-forward" a specified number of blocks: +``` +leo devnode advance N +``` +for an integer `N`. + +The default setting will initialize the server to the latest Consensus Version. + +To terminate the Devnode, simply use `ctrl + c`. + +## Example +The example code in the `index.js` file deploys a sample program to the Devnode, then submits an upgrade transaction which adds a new method to the existing program, and then submits an execution transaction that uses the new method. +The code can be run using: +```bash +yarn dev +``` +Note: The Devnode support programs with dependencies, but the SDK's Devnode deploy method does not currently support nested deployments. Each dependency must be deployed independently using the `buildDevnodeDeploymentTransaction` method. + + + + diff --git a/create-leo-app/template-devnode-js/_gitignore b/create-leo-app/template-devnode-js/_gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/create-leo-app/template-devnode-js/_gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/create-leo-app/template-devnode-js/index.js b/create-leo-app/template-devnode-js/index.js new file mode 100644 index 000000000..2393828b6 --- /dev/null +++ b/create-leo-app/template-devnode-js/index.js @@ -0,0 +1,76 @@ +#!/usr/bin/env ts-node +import { Account, ProgramManager, initThreadPool, NetworkRecordProvider, AleoNetworkClient, getOrInitConsensusVersionTestHeights } from '@provablehq/sdk'; + +const program = `program test_program.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq program_owner aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px;`; + +const upgraded_program = `program test_program.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +function new_transition: + input r0 as u32.private; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq program_owner aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px;`; + +async function main() { + // Initialize multi-threading to allow WASM execution. + await initThreadPool(); + const heights = getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + + const privateKey = "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH"; + const account = new Account({privateKey}); + const networkClient = new AleoNetworkClient("http://localhost:3030"); + const recordProvider = new NetworkRecordProvider(account, networkClient); + + const programManager = new ProgramManager("http://localhost:3030", recordProvider ); + programManager.setAccount(account); + + + const tx_1 = await programManager.buildDevnodeDeploymentTransaction({program: program, priorityFee: 0, privateFee: false}); + console.log("Program deployed - response:", tx_1.toString()); + await programManager.networkClient.submitTransaction(tx_1); + + const tx_2 = await programManager.buildDevnodeUpgradeTransaction({program: upgraded_program, priorityFee: 0, privateFee: false}); + await programManager.networkClient.submitTransaction(tx_2); + console.log("Program deployed - response:", tx_2); + + + const tx_3 = await programManager.buildDevnodeExecutionTransaction({ + privateKey: account.privateKey(), + programName: "test_program.aleo", + functionName: "new_transition", + privateFee: false, + inputs: ["2u32", "4u32"], + priorityFee: 0.0, + edition: 0, + skipProof: true, + }); + await programManager.networkClient.submitTransaction(tx_3); + console.log("Transaction submitted - response:", tx_3.toString()); +} + + +main() + .then(_ => { + }) + .catch(err => { + console.log(err) + process.exit(1) + }); \ No newline at end of file diff --git a/create-leo-app/template-devnode-js/package.json b/create-leo-app/template-devnode-js/package.json new file mode 100644 index 000000000..8332009ab --- /dev/null +++ b/create-leo-app/template-devnode-js/package.json @@ -0,0 +1,12 @@ +{ + "name": "devnode-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.12" + } +} diff --git a/docs/api_reference/sdk-src_program-manager.md b/docs/api_reference/sdk-src_program-manager.md index da4c935a7..6c46e038c 100644 --- a/docs/api_reference/sdk-src_program-manager.md +++ b/docs/api_reference/sdk-src_program-manager.md @@ -238,7 +238,7 @@ programManager.setAccount(Account); const priorityFee = 0.0; // Create the deployment transaction. -const tx = await programManager.buildDeploymentTransaction({program: program, priorityFee: fee, privateFee: false}); +const tx = await programManager.buildUpgradeTransaction({program: program, priorityFee: fee, privateFee: false}); await programManager.networkClient.submitTransaction(tx); // Verify the transaction was successful @@ -1536,6 +1536,146 @@ const baseFeeCredits = Number(baseFeeMicrocredits)/1000000; --- +### `buildDevnodeExecutionTransaction(options) ► Promise.` + +![modifier: public](images/badges/modifier-public.svg) + +Builds an execution transaction for submission to the a local devnode. +This method skips proof generation and is not meant for use with the mainnet or testnet Aleo networks. +Note: getOrInitConsensusVersionTestHeights must be called prior to using this method for this method to work properly. + +Parameters | Type | Description +--- | --- | --- +__options__ | `ExecuteOptions` | *The options for the execution transaction.* +__*return*__ | `Promise.` | *- A promise that resolves to the transaction or an error.* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { AleoKeyProvider, getOrInitConsensusVersionTestHeights, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + +// Initialize the development consensus heights in order to work with devnode. +getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + +// Create a new NetworkClient and RecordProvider. +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager. +const programManager = new ProgramManager("http://localhost:3030", recordProvider); + +// Build and execute the transaction. +const tx = await programManager.buildDevnodeExecutionTransaction({ + programName: "hello_hello.aleo", + functionName: "hello_hello", + priorityFee: 0.0, + privateFee: false, + inputs: ["5u32", "5u32"], +}); + +// Submit the transaction to the network +await programManager.networkClient.submitTransaction(tx.toString()); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 10000); +``` + +--- + +### `buildDevnodeDeploymentTransaction(options) ► string` + +![modifier: public](images/badges/modifier-public.svg) + +Builds a deployment transaction with placeholder certificates and verifying keys for each function in the program. +Intended for use with a local devnode. +`getOrInitConsensusVersionTestHeights` must be called with development heights prior to invoking this method for it to work properly. + +Parameters | Type | Description +--- | --- | --- +__options__ | `DeployOptions` | *The options for the deployment transaction.* +__*return*__ | `string` | *The transaction id of the deployed program or a failure message from the network* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { ProgramManager, NetworkRecordProvider, getOrInitConsensusVersionTestHeights } from "@provablehq/sdk/mainnet.js"; + +// Initialize the development consensus heights in order to work with a local devnode. +getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + +// Create a new NetworkClient, and RecordProvider +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager with the key provider to automatically fetch keys for deployments +const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const programManager = new ProgramManager("http://localhost:3030", recordProvider); +programManager.setAccount(Account); + +// Define a fee in credits +const priorityFee = 0.0; + +// Create the deployment transaction. +const tx = await programManager.buildDevnodeDeploymentTransaction({program: program, fee: priorityFee, privateFee: false}); +await programManager.networkClient.submitTransaction(tx); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 20000); +``` + +--- + +### `buildDevnodeUpgradeTransaction(options) ► string` + +![modifier: public](images/badges/modifier-public.svg) + +Builds an upgrade transaction on a local devnodewith placeholder certificates and verifying keys for each function in the program. +This method is only intended for use with a local devnode. + +Parameters | Type | Description +--- | --- | --- +__options__ | `DeployOptions` | *The options for the deployment transaction.* +__*return*__ | `string` | *The transaction id of the deployed program or a failure message from the network* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + +// Create a new NetworkClient, and RecordProvider +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager with the key provider to automatically fetch keys for deployments +const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const programManager = new ProgramManager("http://localhost:3030", recordProvider); +programManager.setAccount(Account); + +// Define a fee in credits +const priorityFee = 0.0; + +// Create the deployment transaction. +const tx = await programManager.buildDevnodeUpgradeTransaction({program: program, fee: priorityFee, privateFee: false}); +await programManager.networkClient.submitTransaction(tx); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 20000); +``` + +--- + ### `checkFee(address, feeAmount)` ![modifier: public](images/badges/modifier-public.svg) @@ -1753,7 +1893,7 @@ programManager.setAccount(Account); const priorityFee = 0.0; // Create the deployment transaction. -const tx = await programManager.buildDeploymentTransaction({program: program, priorityFee: fee, privateFee: false}); +const tx = await programManager.buildUpgradeTransaction({program: program, priorityFee: fee, privateFee: false}); await programManager.networkClient.submitTransaction(tx); // Verify the transaction was successful @@ -3052,3 +3192,143 @@ const baseFeeCredits = Number(baseFeeMicrocredits)/1000000; ``` --- + +### `buildDevnodeExecutionTransaction(options) ► Promise.` + +![modifier: public](images/badges/modifier-public.svg) + +Builds an execution transaction for submission to the a local devnode. +This method skips proof generation and is not meant for use with the mainnet or testnet Aleo networks. +Note: getOrInitConsensusVersionTestHeights must be called prior to using this method for this method to work properly. + +Parameters | Type | Description +--- | --- | --- +__options__ | `ExecuteOptions` | *The options for the execution transaction.* +__*return*__ | `Promise.` | *- A promise that resolves to the transaction or an error.* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { AleoKeyProvider, getOrInitConsensusVersionTestHeights, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + +// Initialize the development consensus heights in order to work with devnode. +getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + +// Create a new NetworkClient and RecordProvider. +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager. +const programManager = new ProgramManager("http://localhost:3030", recordProvider); + +// Build and execute the transaction. +const tx = await programManager.buildDevnodeExecutionTransaction({ + programName: "hello_hello.aleo", + functionName: "hello_hello", + priorityFee: 0.0, + privateFee: false, + inputs: ["5u32", "5u32"], +}); + +// Submit the transaction to the network +await programManager.networkClient.submitTransaction(tx.toString()); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 10000); +``` + +--- + +### `buildDevnodeDeploymentTransaction(options) ► string` + +![modifier: public](images/badges/modifier-public.svg) + +Builds a deployment transaction with placeholder certificates and verifying keys for each function in the program. +Intended for use with a local devnode. +`getOrInitConsensusVersionTestHeights` must be called with development heights prior to invoking this method for it to work properly. + +Parameters | Type | Description +--- | --- | --- +__options__ | `DeployOptions` | *The options for the deployment transaction.* +__*return*__ | `string` | *The transaction id of the deployed program or a failure message from the network* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { ProgramManager, NetworkRecordProvider, getOrInitConsensusVersionTestHeights } from "@provablehq/sdk/mainnet.js"; + +// Initialize the development consensus heights in order to work with a local devnode. +getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + +// Create a new NetworkClient, and RecordProvider +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager with the key provider to automatically fetch keys for deployments +const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const programManager = new ProgramManager("http://localhost:3030", recordProvider); +programManager.setAccount(Account); + +// Define a fee in credits +const priorityFee = 0.0; + +// Create the deployment transaction. +const tx = await programManager.buildDevnodeDeploymentTransaction({program: program, fee: priorityFee, privateFee: false}); +await programManager.networkClient.submitTransaction(tx); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 20000); +``` + +--- + +### `buildDevnodeUpgradeTransaction(options) ► string` + +![modifier: public](images/badges/modifier-public.svg) + +Builds an upgrade transaction on a local devnodewith placeholder certificates and verifying keys for each function in the program. +This method is only intended for use with a local devnode. + +Parameters | Type | Description +--- | --- | --- +__options__ | `DeployOptions` | *The options for the deployment transaction.* +__*return*__ | `string` | *The transaction id of the deployed program or a failure message from the network* + +#### Examples + +```javascript +/// Import the mainnet version of the sdk. +import { ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + +// Create a new NetworkClient, and RecordProvider +const recordProvider = new NetworkRecordProvider(account, networkClient); +keyProvider.useCache(true); + +// Initialize a program manager with the key provider to automatically fetch keys for deployments +const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const programManager = new ProgramManager("http://localhost:3030", recordProvider); +programManager.setAccount(Account); + +// Define a fee in credits +const priorityFee = 0.0; + +// Create the deployment transaction. +const tx = await programManager.buildDevnodeUpgradeTransaction({program: program, fee: priorityFee, privateFee: false}); +await programManager.networkClient.submitTransaction(tx); + +// Verify the transaction was successful +setTimeout(async () => { + const transaction = await programManager.networkClient.getTransaction(tx.id()); + assert(transaction.id() === tx.id()); +}, 20000); +``` + +--- diff --git a/sdk/src/program-manager.ts b/sdk/src/program-manager.ts index 9eace8f8d..8dbc0b7de 100644 --- a/sdk/src/program-manager.ts +++ b/sdk/src/program-manager.ts @@ -3248,6 +3248,442 @@ class ProgramManager { } } } + + /** + * Builds an execution transaction for submission to the a local devnode. + * This method skips proof generation and is not meant for use with the mainnet or testnet Aleo networks. + * Note: getOrInitConsensusVersionTestHeights must be called prior to using this method for this method to work properly. + * + * @param {ExecuteOptions} options - The options for the execution transaction. + * @returns {Promise} - A promise that resolves to the transaction or an error. + * + * @example + * /// Import the mainnet version of the sdk. + * import { AleoKeyProvider, getOrInitConsensusVersionTestHeights, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + * + * // Initialize the development consensus heights in order to work with devnode. + * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + * + * // Create a new NetworkClient and RecordProvider. + * const recordProvider = new NetworkRecordProvider(account, networkClient); + * keyProvider.useCache(true); + * + * // Initialize a program manager. + * const programManager = new ProgramManager("http://localhost:3030", recordProvider); + * + * // Build and execute the transaction. + * const tx = await programManager.buildDevnodeExecutionTransaction({ + * programName: "hello_hello.aleo", + * functionName: "hello_hello", + * priorityFee: 0.0, + * privateFee: false, + * inputs: ["5u32", "5u32"], + * }); + * + * // Submit the transaction to the network + * await programManager.networkClient.submitTransaction(tx.toString()); + * + * // Verify the transaction was successful + * setTimeout(async () => { + * const transaction = await programManager.networkClient.getTransaction(tx.id()); + * assert(transaction.id() === tx.id()); + * }, 10000); + */ + async buildDevnodeExecutionTransaction( + options: ExecuteOptions, + ): Promise { + // Destructure the options object to access the parameters + const { + functionName, + priorityFee, + privateFee, + inputs, + recordSearchParams, + privateKey, + } = options; + + let feeRecord = options.feeRecord; + let program = options.program; + let programName = options.programName; + let imports = options.imports; + let edition = options.edition; + + let programObject; + // Ensure the function exists on the network + if (program === undefined) { + try { + programObject = await this.networkClient.getProgramObject(programName); + program = programObject.toString(); + } catch (e: any) { + logAndThrow( + `Error finding ${programName}. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network the program is deployed to the network.`, + ); + } + } else if (typeof program == "string") { + try { + programObject = Program.fromString(program); + } catch (e: any) { + logAndThrow(`Program sources passed for ${programName} were invalid: ${e}`); + } + } else if (program instanceof Program) { + programObject = program; + program = program.toString(); + } + + if (!(programObject instanceof Program)) { + logAndThrow(`Failed to validate program ${programName}`); + } + + // Get the program name if it is not provided in the parameters. + if (programName === undefined) { + programName = programObject.id(); + } + + if (edition == undefined) { + try { + edition = await this.networkClient.getLatestProgramEdition(programName); + } catch (e: any) { + console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + edition = 0; + } + } + + // Get the private key from the account if it is not provided in the parameters. + let executionPrivateKey = privateKey; + if ( + typeof privateKey === "undefined" && + typeof this.account !== "undefined" + ) { + executionPrivateKey = this.account.privateKey(); + } + + if (typeof executionPrivateKey === "undefined") { + throw "No private key provided and no private key set in the ProgramManager"; + } + + // Get the fee record from the account if it is not provided in the parameters. + try { + if (privateFee) { + let fee = priorityFee; + // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. + if (!feeRecord) { + console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + const programString = programObject.toString(); + const imports = await this.networkClient.getProgramImports(programString); + const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + fee = baseFee + priorityFee; + } + + // Get a credits.aleo record for the fee. + feeRecord = await this.getCreditsRecord( + fee, + [], + feeRecord, + recordSearchParams + ) + } else { + // If it's specified NOT to use a privateFee, use a public fee. + feeRecord = undefined + } + } catch (e: any) { + logAndThrow( + `Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`, + ); + } + + // Resolve the program imports if they exist. + const numberOfImports = programObject.getImports().length; + if (numberOfImports > 0 && !imports) { + try { + imports = ( + await this.networkClient.getProgramImports(programName) + ); + } catch (e: any) { + logAndThrow( + `Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`, + ); + } + } + + // Build a transaction without a proof + return await WasmProgramManager.buildDevnodeExecutionTransaction( + executionPrivateKey, + program, + functionName, + inputs, + priorityFee, + feeRecord, + this.host, + imports, + edition + ); + } + + /** + * Builds a deployment transaction with placeholder certificates and verifying keys for each function in the program. + * Intended for use with a local devnode. + * `getOrInitConsensusVersionTestHeights` must be called with development heights prior to invoking this method for it to work properly. + * + * @param {DeployOptions} options - The options for the deployment transaction. + * @returns {string} The transaction id of the deployed program or a failure message from the network + * + * @example + * /// Import the mainnet version of the sdk. + * import { ProgramManager, NetworkRecordProvider, getOrInitConsensusVersionTestHeights } from "@provablehq/sdk/mainnet.js"; + * + * // Initialize the development consensus heights in order to work with a local devnode. + * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11"); + * + * // Create a new NetworkClient, and RecordProvider + * const recordProvider = new NetworkRecordProvider(account, networkClient); + * keyProvider.useCache(true); + * + * // Initialize a program manager with the key provider to automatically fetch keys for deployments + * const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; + * const programManager = new ProgramManager("http://localhost:3030", recordProvider); + * programManager.setAccount(Account); + * + * // Define a fee in credits + * const priorityFee = 0.0; + * + * // Create the deployment transaction. + * const tx = await programManager.buildDevnodeDeploymentTransaction({program: program, fee: priorityFee, privateFee: false}); + * await programManager.networkClient.submitTransaction(tx); + * + * // Verify the transaction was successful + * setTimeout(async () => { + * const transaction = await programManager.networkClient.getTransaction(tx.id()); + * assert(transaction.id() === tx.id()); + * }, 20000); + */ + async buildDevnodeDeploymentTransaction( + options: DeployOptions + ): Promise { + const { program, priorityFee, privateFee, recordSearchParams } = options; + let feeRecord = options.feeRecord; + let privateKey = options.privateKey; + + // Ensure the program is valid. + let programObject; + try { + programObject = Program.fromString(program); + } catch (e: any) { + logAndThrow( + `Error parsing program: '${e.message}'. Please ensure the program is valid.`, + ); + } + + // Ensure the program is valid and does not exist on the network + try { + let programSource; + try { + programSource = await this.networkClient.getProgram( + programObject.id(), + ); + } catch (e) { + // Program does not exist on the network, deployment can proceed + console.log( + `Program ${programObject.id()} does not exist on the network, deploying...`, + ); + } + if (typeof programSource === "string") { + throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`); + } + } catch (e: any) { + logAndThrow(`Error validating program: ${e.message}`); + } + + // Get the private key from the account if it is not provided in the parameters + let deploymentPrivateKey = privateKey; + if ( + typeof privateKey === "undefined" && + typeof this.account !== "undefined" + ) { + deploymentPrivateKey = this.account.privateKey(); + } + + if (typeof deploymentPrivateKey === "undefined") { + throw "No private key provided and no private key set in the ProgramManager"; + } + + // Get the fee record from the account if it is not provided in the parameters + try { + if (privateFee) { + let fee = priorityFee; + // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. + if (!feeRecord) { + console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + const programString = programObject.toString(); + const imports = await this.networkClient.getProgramImports(programString); + const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + fee = baseFee + priorityFee; + } + + // Get a credits.aleo record for the fee. + feeRecord = await this.getCreditsRecord( + fee, + [], + feeRecord, + recordSearchParams + ) + } else { + // If it's specified NOT to use a privateFee, use a public fee. + feeRecord = undefined + } + } catch (e: any) { + logAndThrow( + `Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`, + ); + } + + // Resolve the program imports if they exist + let imports; + try { + imports = await this.networkClient.getProgramImports(program); + } catch (e: any) { + logAndThrow( + `Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`, + ); + } + + return await WasmProgramManager.buildDevnodeDeploymentTransaction( + deploymentPrivateKey, + program, + priorityFee, + feeRecord, + this.host, + imports, + ); + } + + /** + * Builds an upgrade transaction on a local devnodewith placeholder certificates and verifying keys for each function in the program. + * This method is only intended for use with a local devnode. + * + * @param {DeployOptions} options - The options for the deployment transaction. + * @returns {string} The transaction id of the deployed program or a failure message from the network + * + * @example + * /// Import the mainnet version of the sdk. + * import { ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; + * + * // Create a new NetworkClient, and RecordProvider + * const recordProvider = new NetworkRecordProvider(account, networkClient); + * keyProvider.useCache(true); + * + * // Initialize a program manager with the key provider to automatically fetch keys for deployments + * const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; + * const programManager = new ProgramManager("http://localhost:3030", recordProvider); + * programManager.setAccount(Account); + * + * // Define a fee in credits + * const priorityFee = 0.0; + * + * // Create the deployment transaction. + * const tx = await programManager.buildDevnodeUpgradeTransaction({program: program, fee: priorityFee, privateFee: false}); + * await programManager.networkClient.submitTransaction(tx); + * + * // Verify the transaction was successful + * setTimeout(async () => { + * const transaction = await programManager.networkClient.getTransaction(tx.id()); + * assert(transaction.id() === tx.id()); + * }, 20000); + */ + async buildDevnodeUpgradeTransaction( + options: DeployOptions + ): Promise { + const { program, priorityFee, privateFee, recordSearchParams } = options; + let feeRecord = options.feeRecord; + let privateKey = options.privateKey; + + // Ensure the program is valid. + let programObject; + try { + programObject = Program.fromString(program); + } catch (e: any) { + logAndThrow( + `Error parsing program: '${e.message}'. Please ensure the program is valid.`, + ); + } + + // Ensure the program is valid and does not exist on the network. + try { + let programSource; + try { + programSource = await this.networkClient.getProgram( + programObject.id(), + ); + } catch (e) { + // Program does not exist on the network. + logAndThrow( + `Program ${programObject.id()} does not exist on the network...`, + ); + } + } catch (e: any) { + logAndThrow(`Error validating program: ${e.message}`); + } + + // Get the private key from the account if it is not provided in the parameters + let deploymentPrivateKey = privateKey; + if ( + typeof privateKey === "undefined" && + typeof this.account !== "undefined" + ) { + deploymentPrivateKey = this.account.privateKey(); + } + + if (typeof deploymentPrivateKey === "undefined") { + throw "No private key provided and no private key set in the ProgramManager"; + } + + // Get the fee record from the account if it is not provided in the parameters + try { + if (privateFee) { + let fee = priorityFee; + // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. + if (!feeRecord) { + console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + const programString = programObject.toString(); + const imports = await this.networkClient.getProgramImports(programString); + const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + fee = baseFee + priorityFee; + } + + // Get a credits.aleo record for the fee. + feeRecord = await this.getCreditsRecord( + fee, + [], + feeRecord, + recordSearchParams + ) + } else { + // If it's specified NOT to use a privateFee, use a public fee. + feeRecord = undefined + } + } catch (e: any) { + logAndThrow( + `Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`, + ); + } + + // Resolve the program imports if they exist + let imports; + try { + imports = await this.networkClient.getProgramImports(program); + } catch (e: any) { + logAndThrow( + `Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`, + ); + } + return WasmProgramManager.buildDevnodeUpgradeTransaction( + deploymentPrivateKey, + program, + priorityFee, + feeRecord, + this.host, + imports, + ); + } } // Ensure the transfer type requires an amount record diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 9e5f97df6..2c2bc0e8d 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -2187,7 +2187,7 @@ dependencies = [ [[package]] name = "snarkvm-algorithms" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2214,7 +2214,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-account", "snarkvm-circuit-algorithms", @@ -2228,7 +2228,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-account" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-network", "snarkvm-circuit-types", @@ -2238,7 +2238,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-algorithms" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-types", "snarkvm-console-algorithms", @@ -2248,7 +2248,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-collections" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-algorithms", "snarkvm-circuit-types", @@ -2258,7 +2258,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-environment" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "indexmap", "itertools", @@ -2276,12 +2276,12 @@ dependencies = [ [[package]] name = "snarkvm-circuit-environment-witness" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" [[package]] name = "snarkvm-circuit-network" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-algorithms", "snarkvm-circuit-collections", @@ -2292,7 +2292,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-program" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-account", "snarkvm-circuit-algorithms", @@ -2306,7 +2306,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-address", @@ -2321,7 +2321,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-address" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2334,7 +2334,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-boolean" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-console-types-boolean", @@ -2343,7 +2343,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-field" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2353,7 +2353,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-group" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2365,7 +2365,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-integers" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2377,7 +2377,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-scalar" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2388,7 +2388,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-string" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -2400,7 +2400,7 @@ dependencies = [ [[package]] name = "snarkvm-console" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-account", "snarkvm-console-algorithms", @@ -2413,7 +2413,7 @@ dependencies = [ [[package]] name = "snarkvm-console-account" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "bs58", "snarkvm-console-network", @@ -2424,7 +2424,7 @@ dependencies = [ [[package]] name = "snarkvm-console-algorithms" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "blake2s_simd", "hex", @@ -2439,7 +2439,7 @@ dependencies = [ [[package]] name = "snarkvm-console-collections" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "rayon", @@ -2450,7 +2450,7 @@ dependencies = [ [[package]] name = "snarkvm-console-network" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "anyhow", "enum-iterator", @@ -2470,7 +2470,7 @@ dependencies = [ [[package]] name = "snarkvm-console-network-environment" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "anyhow", "bech32", @@ -2488,7 +2488,7 @@ dependencies = [ [[package]] name = "snarkvm-console-program" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "enum-iterator", "enum_index", @@ -2509,7 +2509,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-address", @@ -2524,7 +2524,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-address" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2535,7 +2535,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-boolean" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", ] @@ -2543,7 +2543,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-field" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2553,7 +2553,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-group" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2564,7 +2564,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-integers" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2575,7 +2575,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-scalar" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2586,7 +2586,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-string" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -2597,7 +2597,7 @@ dependencies = [ [[package]] name = "snarkvm-curves" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "rand", "rayon", @@ -2611,7 +2611,7 @@ dependencies = [ [[package]] name = "snarkvm-fields" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2628,7 +2628,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-authority" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "anyhow", "rand", @@ -2640,7 +2640,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-block" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "anyhow", "indexmap", @@ -2662,7 +2662,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-committee" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "indexmap", "rayon", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-batch-certificate" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "indexmap", "rayon", @@ -2687,7 +2687,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-batch-header" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "indexmap", "rayon", @@ -2699,7 +2699,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-data" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "bytes", "serde_json", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-subdag" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "indexmap", "rayon", @@ -2724,7 +2724,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-transmission-id" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "snarkvm-console", "snarkvm-ledger-puzzle", @@ -2733,7 +2733,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-puzzle" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2752,7 +2752,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-puzzle-epoch" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2774,7 +2774,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-query" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "anyhow", "async-trait", @@ -2791,7 +2791,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-store" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std-storage", "anyhow", @@ -2810,12 +2810,13 @@ dependencies = [ "snarkvm-synthesizer-program", "snarkvm-synthesizer-snark", "snarkvm-utilities", + "tracing", ] [[package]] name = "snarkvm-parameters" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2840,7 +2841,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", @@ -2865,13 +2866,14 @@ dependencies = [ "snarkvm-synthesizer-program", "snarkvm-synthesizer-snark", "snarkvm-utilities", + "tokio", "tracing", ] [[package]] name = "snarkvm-synthesizer-process" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "colored", @@ -2895,7 +2897,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer-program" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "enum-iterator", "indexmap", @@ -2914,7 +2916,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer-snark" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "bincode", "serde_json", @@ -2927,11 +2929,12 @@ dependencies = [ [[package]] name = "snarkvm-utilities" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "aleo-std", "anyhow", "bincode", + "colored", "num-bigint", "num_cpus", "rand", @@ -2949,7 +2952,7 @@ dependencies = [ [[package]] name = "snarkvm-utilities-derives" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "proc-macro2", "quote 1.0.38", @@ -2959,7 +2962,7 @@ dependencies = [ [[package]] name = "snarkvm-wasm" version = "4.3.0" -source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=21279b985#21279b9850d989c44008acf2fa7b3e90454d460a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?rev=6c9efe3#6c9efe3c3d3b66e4611e13520c65c37de16b629e" dependencies = [ "getrandom", "snarkvm-console", diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 23fc5413b..1f39b1198 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -23,16 +23,16 @@ doctest = false [dependencies.snarkvm-algorithms] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" [dependencies.snarkvm-circuit-network] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" features = ["wasm"] [dependencies.snarkvm-console] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" default-features = false features = [ "account", @@ -46,37 +46,37 @@ features = [ [dependencies.snarkvm-ledger-block] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" features = ["wasm"] [dependencies.snarkvm-ledger-query] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" features = ["async", "wasm"] [dependencies.snarkvm-ledger-store] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" [dependencies.snarkvm-parameters] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" default-features = false features = ["wasm"] [dependencies.snarkvm-synthesizer-program] +rev = "6c9efe3" git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" features = ["wasm"] [dependencies.snarkvm-synthesizer] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" features = ["async", "wasm"] [dependencies.snarkvm-wasm] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "21279b985" +rev = "6c9efe3" features = ["fields", "utilities"] [dependencies.anyhow] diff --git a/wasm/src/programs/manager/deploy.rs b/wasm/src/programs/manager/deploy.rs index 4da053c86..c3ee4c970 100644 --- a/wasm/src/programs/manager/deploy.rs +++ b/wasm/src/programs/manager/deploy.rs @@ -26,11 +26,15 @@ use crate::{ get_program_from_network, latest_block_height, latest_program_edition, + latest_stateroot, log, types::native::{ AddressNative, + CertificateNative, CurrentAleo, CurrentNetwork, + DeploymentNative, + FeeNative, PrivateKeyNative, ProcessNative, ProgramNative, @@ -307,4 +311,278 @@ impl ProgramManager { TransactionNative::from_deployment(owner, deployment, fee).map_err(|err| err.to_string())?, )) } + + /// Deploy an Aleo program without synthesizing keys and generating certificates. + /// Intended for use with Leo Devnode. + /// + /// @param private_key The private key of the sender + /// @param program The source code of the program being deployed + /// @param imports A javascript object holding the source code of any imported programs in the + /// form \{"program_name1": "program_source_code", "program_name2": "program_source_code", ..\}. + /// Note that all imported programs must be deployed on chain before the main program in order + /// for the deployment to succeed + /// @param priority_fee_credits The optional priority fee to be paid for the transaction + /// @param fee_record The record to spend the fee from + /// @param url The url of the Aleo network node to send the transaction to + /// @param imports (optional) Provide a list of imports to use for the program deployment in the + /// form of a javascript object where the keys are a string of the program name and the values + /// are a string representing the program source code \{ "hello.aleo": "hello.aleo source code" \} + /// @param fee_proving_key (optional) Provide a proving key to use for the fee execution + /// @param fee_verifying_key (optional) Provide a verifying key to use for the fee execution + /// @returns {Transaction} + #[wasm_bindgen(js_name = buildDevnodeDeploymentTransaction)] + #[allow(clippy::too_many_arguments)] + pub async fn devnode_deploy( + private_key: &PrivateKey, + program: &str, + priority_fee_credits: f64, + fee_record: Option, + url: Option, + imports: Option, + ) -> Result { + log("Creating deployment transaction"); + let mut process_native = ProcessNative::load_web().map_err(|err| err.to_string())?; + let process = &mut process_native; + + log("Checking program has a valid name"); + let program = ProgramNative::from_str(program).map_err(|err| err.to_string())?; + let program_id = program.id(); + + log("Checking program imports are valid and add them to the process"); + ProgramManager::resolve_imports(process, &program, imports)?; + let rng = &mut StdRng::from_entropy(); + + log("Creating deployment"); + let node_url = url.as_deref().unwrap_or(LOCAL_URL); + + // Create deployment without synthesizing keys and generating certificates. + if program.functions().is_empty() { + log("Program must have at least one function"); + Err("Program must have at least one function".to_string())?; + } + let mut verifying_keys = Vec::with_capacity(program.functions().len()); + for function_name in program.functions().keys() { + let (verifying_key, certificate) = { + // Sample a dummy verifying key for each function. + let verifying_key = + VerifyingKeyNative::from_str(DEVNODE_VERIFIER_KEY).map_err(|err| err.to_string())?; + + // Sample a dummy certificate. + let certificate = CertificateNative::from_str(DEVNODE_CERTIFICATE).map_err(|err| err.to_string())?; + (verifying_key, certificate) + }; + verifying_keys.push((*function_name, (verifying_key, certificate))); + } + + let edition_response = latest_program_edition(node_url, &program_id.to_string()).await; + let edition = match edition_response { + Ok(edition) => edition + 1, + Err(_) => 0, + }; + + let mut deployment = DeploymentNative::new(edition, program.clone(), verifying_keys, None, None) + .map_err(|err| err.to_string())?; + + let latest_height = latest_block_height(node_url).await.map_err(|err| err.to_string())?; + let consensus_version = CurrentNetwork::CONSENSUS_VERSION(latest_height).map_err(|err| err.to_string())?; + + let private_key_native = PrivateKeyNative::from(private_key); + if consensus_version < ConsensusVersion::V9 { + deployment.set_program_checksum_raw(None); + deployment.set_program_owner_raw(None) + } else { + deployment.set_program_checksum_raw(Some(deployment.program().to_checksum())); + deployment + .set_program_owner_raw(Some(AddressNative::try_from(private_key_native).map_err(|e| e.to_string())?)); + } + + let deployment_id = deployment.to_deployment_id().map_err(|e| e.to_string())?; + + let owner = ProgramOwnerNative::new(private_key, deployment_id, rng).map_err(|err| err.to_string())?; + + // Construct the fee authorization for the deployment + let (minimum_deployment_cost, _) = deployment_cost::(process, &deployment, consensus_version) + .map_err(|err| err.to_string())?; + + // Check to see if the fee record has enough microcredits to pay for the deployment. + let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64; + Self::validate_fee_record(&fee_record, minimum_deployment_cost, priority_fee_microcredits) + .map_err(|e| e.to_string())?; + + let fee_authorization = match fee_record { + Some(record) => process + .authorize_fee_private::( + &private_key, + record.into(), + minimum_deployment_cost, + priority_fee_microcredits, + deployment_id, + rng, + ) + .map_err(|e| e.to_string())?, + None => process + .authorize_fee_public::( + &private_key, + minimum_deployment_cost, + priority_fee_microcredits, + deployment_id, + rng, + ) + .map_err(|e| e.to_string())?, + }; + + // Get the state root. + let state_root = latest_stateroot(node_url).await.map_err(|e| e.to_string()).map_err(|e| e.to_string())?; + + let fee = FeeNative::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None) + .map_err(|err| err.to_string())?; + + log("Creating deployment transaction"); + Ok(Transaction::from( + TransactionNative::from_deployment(owner, deployment, fee) + .map_err(|err| err.to_string()) + .map_err(|e| e.to_string())?, + )) + } + + /// Upgrade a deployed Aleo program without synthesizing keys and generating certificates. + /// Intended for use with Leo Devnode. + /// + /// @param private_key The private key of the sender + /// @param program The source code of the upgraded program + /// @param imports A javascript object holding the source code of any imported programs in the + /// form \{"program_name1": "program_source_code", "program_name2": "program_source_code", ..\}. + /// Note that all imported programs must be deployed on chain before the main program in order + /// for the deployment to succeed + /// @param priority_fee_credits The optional priority fee to be paid for the transaction + /// @param fee_record The record to spend the fee from + /// @param url The url of the Aleo network node to send the transaction to + /// @param imports (optional) Provide a list of imports to use for the program deployment in the + /// form of a javascript object where the keys are a string of the program name and the values + /// are a string representing the program source code \{ "hello.aleo": "hello.aleo source code" \} + /// @param fee_proving_key (optional) Provide a proving key to use for the fee execution + /// @param fee_verifying_key (optional) Provide a verifying key to use for the fee execution + /// @returns {Transaction} + #[wasm_bindgen(js_name = buildDevnodeUpgradeTransaction)] + #[allow(clippy::too_many_arguments)] + pub async fn devnode_upgrade( + private_key: &PrivateKey, + program: &str, + priority_fee_credits: f64, + fee_record: Option, + url: Option, + imports: Option, + ) -> Result { + log("Creating deployment transaction"); + let mut process_native = ProcessNative::load_web().map_err(|err| err.to_string())?; + let process = &mut process_native; + + log("Checking program imports are valid and add them to the process"); + let program = ProgramNative::from_str(&program).map_err(|err| err.to_string())?; + ProgramManager::resolve_imports(process, &program, imports)?; + let rng = &mut StdRng::from_entropy(); + + log("Creating deployment"); + let node_url = url.as_deref().unwrap_or(LOCAL_URL); + + log("Checking if the program is deployed on the network"); + let program_id = program.id().to_string(); + let program_string = get_program_from_network(node_url, &program_id).await.map_err(|err| err.to_string())?; + let deployed_program = ProgramNative::from_str(&program_string).map_err(|err| err.to_string())?; + let deployed_program_edition = + latest_program_edition(node_url, &program.id().to_string()).await.map_err(|err| err.to_string())?; + + // Load the deployed program into the process + log("Adding deployed program to the process"); + process.add_program_with_edition(&deployed_program, deployed_program_edition).map_err(|err| err.to_string())?; + + // Create deployment without synthesizing keys and generating certificates. + if program.functions().is_empty() { + log("Program must have at least one function"); + Err("Program must have at least one function".to_string())?; + } + let mut verifying_keys = Vec::with_capacity(program.functions().len()); + for function_name in program.functions().keys() { + let (verifying_key, certificate) = { + // Sample a dummy verifying key for each function. + let verifying_key = + VerifyingKeyNative::from_str(DEVNODE_VERIFIER_KEY).map_err(|err| err.to_string())?; + + // Sample a dummy certificate. + let certificate = CertificateNative::from_str(DEVNODE_CERTIFICATE).map_err(|err| err.to_string())?; + (verifying_key, certificate) + }; + verifying_keys.push((*function_name, (verifying_key, certificate))); + } + + let edition_response = latest_program_edition(node_url, &program_id.to_string()).await; + let edition = match edition_response { + Ok(edition) => edition + 1, + Err(_) => 0, + }; + + let mut deployment = DeploymentNative::new(edition, program.clone(), verifying_keys, None, None) + .map_err(|err| err.to_string())?; + + let latest_height = latest_block_height(node_url).await.map_err(|err| err.to_string())?; + let consensus_version = CurrentNetwork::CONSENSUS_VERSION(latest_height).map_err(|err| err.to_string())?; + + let private_key_native = PrivateKeyNative::from(private_key); + if consensus_version < ConsensusVersion::V9 { + deployment.set_program_checksum_raw(None); + deployment.set_program_owner_raw(None) + } else { + deployment.set_program_checksum_raw(Some(deployment.program().to_checksum())); + deployment + .set_program_owner_raw(Some(AddressNative::try_from(private_key_native).map_err(|e| e.to_string())?)); + } + + let deployment_id = deployment.to_deployment_id().map_err(|e| e.to_string())?; + + let owner = ProgramOwnerNative::new(private_key, deployment_id, rng).map_err(|err| err.to_string())?; + + // Construct the fee authorization for the deployment + let (minimum_deployment_cost, _) = deployment_cost::(process, &deployment, consensus_version) + .map_err(|err| err.to_string())?; + + // Check to see if the fee record has enough microcredits to pay for the deployment. + let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64; + Self::validate_fee_record(&fee_record, minimum_deployment_cost, priority_fee_microcredits) + .map_err(|e| e.to_string())?; + + let fee_authorization = match fee_record { + Some(record) => process + .authorize_fee_private::( + &private_key, + record.into(), + minimum_deployment_cost, + priority_fee_microcredits, + deployment_id, + rng, + ) + .map_err(|e| e.to_string())?, + None => process + .authorize_fee_public::( + &private_key, + minimum_deployment_cost, + priority_fee_microcredits, + deployment_id, + rng, + ) + .map_err(|e| e.to_string())?, + }; + + // Get the state root. + let state_root = latest_stateroot(node_url).await.map_err(|e| e.to_string()).map_err(|e| e.to_string())?; + + let fee = FeeNative::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None) + .map_err(|err| err.to_string())?; + + log("Creating deployment transaction"); + Ok(Transaction::from( + TransactionNative::from_deployment(owner, deployment, fee) + .map_err(|err| err.to_string()) + .map_err(|e| e.to_string())?, + )) + } } diff --git a/wasm/src/programs/manager/execute.rs b/wasm/src/programs/manager/execute.rs index d0886bee1..af3fcc7e9 100644 --- a/wasm/src/programs/manager/execute.rs +++ b/wasm/src/programs/manager/execute.rs @@ -29,12 +29,15 @@ use crate::{ execute_fee, execute_program, latest_block_height, + latest_stateroot, log, process_inputs, types::native::{ AuthorizationNative, CurrentAleo, CurrentNetwork, + ExecutionNative, + FeeNative, IdentifierNative, LocatorNative, ProcessNative, @@ -462,6 +465,128 @@ impl ProgramManager { Ok(Transaction::from(TransactionNative::from_execution(execution, fee).map_err(|e| e.to_string())?)) } + /// Generate an execution transaction without a proof. + /// Intended for use with the Leo devnode tool. + /// + /// @param private_key The private key of the sender + /// @param program The source code of the program being executed + /// @param function The name of the function to execute + /// @param inputs A javascript array of inputs to the function + /// @param priority_fee_credits The optional priority fee to be paid for the transaction + /// @param fee_record The record to spend the fee from + /// @param url The url of the Aleo network node to send the transaction to + /// If this is set to 'true' the keys synthesized (or passed in as optional parameters via the + /// `proving_key` and `verifying_key` arguments) will be stored in the ProgramManager's memory + /// and used for subsequent transactions. If this is set to 'false' the proving and verifying + /// keys will be deallocated from memory after the transaction is executed. + /// @param imports (optional) Provide a list of imports to use for the function execution in the + /// form of a javascript object where the keys are a string of the program name and the values + /// are a string representing the program source code \{ "hello.aleo": "hello.aleo source code" \} + /// @param proving_key (optional) Provide a verifying key to use for the function execution + /// @param verifying_key (optional) Provide a verifying key to use for the function execution + /// @param fee_proving_key (optional) Provide a proving key to use for the fee execution + /// @param fee_verifying_key (optional) Provide a verifying key to use for the fee execution + /// @param offline_query An offline query object to use if building a transaction without an internet connection. + /// @param edition The edition of the program to execute. Defaults to the latest found on the network, or 1 if the program does not exist on the network. + /// @returns {Transaction} + #[wasm_bindgen(js_name = buildDevnodeExecutionTransaction)] + #[allow(clippy::too_many_arguments)] + pub async fn devnode_execute( + private_key: &PrivateKey, + program: &str, + function: &str, + inputs: Array, + priority_fee_credits: f64, + fee_record: Option, + url: Option, + imports: Option, + edition: Option, + ) -> Result { + log("Loading the SnarkVM process"); + let mut process_native = ProcessNative::load_web().map_err(|err| err.to_string())?; + let process = &mut process_native; + let node_url = url.as_deref().unwrap_or(LOCAL_URL); + + // Initialize the rng. + let rng = &mut StdRng::from_entropy(); + + log("Check program imports are valid and add them to the process"); + let program_native = ProgramNative::from_str(program).map_err(|e| e.to_string())?; + let program_id = program_native.id().to_string(); + ProgramManager::resolve_imports(process, &program_native, imports)?; + let edition = edition.unwrap_or(1); + + let inputs = process_inputs!(inputs); + + // Add the program to the process. + if program_id != "credits.aleo" { + if !process.contains_program(program_native.id()) { + log("Adding program to the process"); + process.add_program_with_edition(&program_native, edition).map_err(|e| e.to_string())?; + } + } + + // Generate the authorization. + let authorization = process + .authorize::(&private_key, &program_id, function, inputs.iter(), rng) + .map_err(|e| e.to_string())?; + + // Get the state root. + let state_root = latest_stateroot(node_url).await.map_err(|e| e.to_string())?; + + // Get the consensus version. + let latest_height = latest_block_height(node_url).await.map_err(|err| err.to_string())?; + let consensus_version = CurrentNetwork::CONSENSUS_VERSION(latest_height).map_err(|err| err.to_string())?; + + // Execute without proving. + let execution = ExecutionNative::from(authorization.transitions().values().cloned(), state_root, None) + .map_err(|e| e.to_string())?; + + // Calculate the cost. + let (cost, _) = execution_cost(&process, &execution, consensus_version).map_err(|e| e.to_string())?; + + // Generate the fee authorization. + let id = authorization.to_execution_id().map_err(|e| e.to_string())?; + + // Convert the priority fee to microcredits. + let priority_fee_microcredits = (priority_fee_credits * 1_000_000.0) as u64; + + let fee_authorization = match fee_record { + Some(fee_record) => { + log("Authorizing credits.aleo/fee_private"); + let fee_record_native = RecordPlaintextNative::from_str(&fee_record.to_string()) + .map_err(|e| format!("Invalid fee record: {}", e))?; + process + .authorize_fee_private::( + &private_key, + fee_record_native, + cost, + priority_fee_microcredits, + id, + rng, + ) + .map_err(|e| e.to_string())? + } + None => { + log("Authorizing credits.aleo/fee_public"); + process + .authorize_fee_public::(&private_key, cost, priority_fee_microcredits, id, rng) + .map_err(|e| e.to_string())? + } + }; + + // Create a fee transition without a proof. + let fee = FeeNative::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None) + .map_err(|e| e.to_string())?; + + // Evaluate the process to ensure validity. + let _response = process.evaluate::(authorization).map_err(|e| e.to_string())?; + + // Create the transaction. + let transaction = TransactionNative::from_execution(execution, Some(fee)).map_err(|e| e.to_string())?; + Ok(Transaction::from(transaction)) + } + /// Estimate Fee for Aleo function execution. Note if "cache" is set to true, the proving and /// verifying keys will be stored in the ProgramManager's memory and used for subsequent /// program executions. diff --git a/wasm/src/programs/manager/mod.rs b/wasm/src/programs/manager/mod.rs index 1517fcfd4..b2460049d 100644 --- a/wasm/src/programs/manager/mod.rs +++ b/wasm/src/programs/manager/mod.rs @@ -23,6 +23,11 @@ mod split; mod transfer; pub const DEFAULT_URL: &str = "https://api.explorer.provable.com/v1"; +pub const LOCAL_URL: &str = "http://localhost:3030"; + +const DEVNODE_VERIFIER_KEY: &str = "verifier1qygqqqqqqqqqqqyvxgqqqqqqqqq87vsqqqqqqqqqhe7sqqqqqqqqqma4qqqqqqqqqq65yqqqqqqqqqqvqqqqqqqqqqqgtlaj49fmrk2d8slmselaj9tpucgxv6awu6yu4pfcn5xa0yy0tpxpc8wemasjvvxr9248vt3509vpk3u60ejyfd9xtvjmudpp7ljq2csk4yqz70ug3x8xp3xn3ul0yrrw0mvd2g8ju7rts50u3smue03gp99j88f0ky8h6fjlpvh58rmxv53mldmgrxa3fq6spsh8gt5whvsyu2rk4a2wmeyrgvvdf29pwp02srktxnvht3k6ff094usjtllggva2ym75xc4lzuqu9xx8ylfkm3qc7lf7ktk9uu9du5raukh828dzgq26hrarq5ajjl7pz7zk924kekjrp92r6jh9dpp05mxtuffwlmvew84dvnqrkre7lw29mkdzgdxwe7q8z0vnkv2vwwdraekw2va3plu7rkxhtnkuxvce0qkgxcxn5mtg9q2c3vxdf2r7jjse2g68dgvyh85q4mzfnvn07lletrpty3vypus00gfu9m47rzay4mh5w9f03z9zgzgzhkv0mupdqsk8naljqm9tc2qqzhf6yp3mnv2ey89xk7sw9pslzzlkndfd2upzmew4e4vnrkr556kexs9qrykkuhsr260mnrgh7uv0sp2meky0keeukaxgjdsnmy77kl48g3swcvqdjm50ejzr7x04vy7hn7anhd0xeetclxunnl7pd6e52qxdlr3nmutz4zr8f2xqa57a2zkl59a28w842cj4783zpy9hxw03k6vz4a3uu7sm072uqknpxjk8fyq4vxtqd08kd93c2mt40lj9ag35nm4rwcfjayejk57m9qqu83qnkrj3sz90pw808srmf705n2yu6gvqazpvu2mwm8x6mgtlsntxfhr0qas43rqxnccft36z4ygty86390t7vrt08derz8368z8ekn3yywxgp4uq24gm6e58tpp0lcvtpsm3nkwpnmzztx4qvkaf6vk38wg787h8mfpqqqqqqqqqqt49m8x"; +const DEVNODE_CERTIFICATE: &str = + "certificate1qyqsqqqqqqqqqqxvwszp09v860w62s2l4g6eqf0kzppyax5we36957ywqm2dplzwvvlqg0kwlnmhzfatnax7uaqt7yqqqw0sc4u"; use crate::{ KeyPair, diff --git a/wasm/src/types/native/mod.rs b/wasm/src/types/native/mod.rs index 77c2f4dd9..978f3edb6 100644 --- a/wasm/src/types/native/mod.rs +++ b/wasm/src/types/native/mod.rs @@ -38,12 +38,12 @@ use snarkvm_console::{ }, types::{Boolean, Field, Group, I8, I16, I32, I64, I128, Scalar, U8, U16, U32, U64, U128}, }; -use snarkvm_ledger_block::{Execution, Input, Output, Transaction, Transition}; +use snarkvm_ledger_block::{Deployment, Execution, Fee, Input, Output, Transaction, Transition}; use snarkvm_synthesizer::{ Authorization, Process, Program, - snark::{ProvingKey, VerifyingKey}, + snark::{Certificate, ProvingKey, VerifyingKey}, }; mod request; @@ -78,6 +78,7 @@ pub type BHP256Native = BHP256; pub type BHP512Native = BHP512; pub type BHP768Native = BHP768; pub type BHP1024Native = BHP1024; +pub type CertificateNative = Certificate; pub type Pedersen64Native = Pedersen64; pub type Pedersen128Native = Pedersen128; pub type Poseidon2Native = Poseidon2; @@ -102,7 +103,9 @@ pub type ResponseNative = Response; pub type ValueNative = Value; // Ledger types +pub type DeploymentNative = Deployment; pub type ExecutionNative = Execution; +pub type FeeNative = Fee; pub type InputNative = Input; pub type OutputNative = Output; pub type ProgramOwnerNative = ProgramOwner;