From 3cd390928d38cc6192d09613f7292c09023252ef Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Wed, 25 Oct 2023 14:03:58 +0200 Subject: [PATCH 1/3] Add offline verification method to the program manager, add verifying key to the Execution Response and make peer urls an optional parameter --- create-aleo-app/template-node/index.js | 7 +- .../template-react-leo/src/workers/worker.js | 4 +- sdk/README.md | 26 ++-- sdk/docs/AleoNetworkClient.html | 2 +- sdk/docs/aleo_network_client.ts.html | 2 +- sdk/src/function-key-provider.ts | 14 +- sdk/src/network-client.ts | 2 +- sdk/src/program-manager.ts | 30 ++++- sdk/src/record-provider.ts | 14 +- sdk/src/worker.ts | 2 +- wasm/src/programs/execution.rs | 13 +- wasm/src/programs/manager/deploy.rs | 5 +- wasm/src/programs/manager/execute.rs | 31 +++-- wasm/src/programs/manager/join.rs | 7 +- wasm/src/programs/manager/mod.rs | 6 +- wasm/src/programs/manager/split.rs | 5 +- wasm/src/programs/manager/transfer.rs | 7 +- wasm/src/programs/response.rs | 121 +++++++++++++----- wasm/tests/offchain.rs | 18 +-- .../src/tabs/develop/execute/LoadProgram.jsx | 2 +- 20 files changed, 203 insertions(+), 115 deletions(-) diff --git a/create-aleo-app/template-node/index.js b/create-aleo-app/template-node/index.js index aee41705c..99fd72610 100644 --- a/create-aleo-app/template-node/index.js +++ b/create-aleo-app/template-node/index.js @@ -42,14 +42,17 @@ async function localProgramExecution(program, aleoFunction, inputs) { ); console.log(executionResponse.getOutputs()) + // Execute a second time to ensure the cached keys are still being used - however, this time prove the execution. executionResponse = await programManager.executeOffline( hello_hello_program, "hello", ["5u32", "5u32"], - false, + true, keyProviderParams, ); - return executionResponse.getOutputs(); + + // Verify the execution using the verifying key that was generated earlier. + programManager.verifyExecution(executionResponse); } const start = Date.now(); diff --git a/create-aleo-app/template-react-leo/src/workers/worker.js b/create-aleo-app/template-react-leo/src/workers/worker.js index 92bad6eed..908ff332d 100644 --- a/create-aleo-app/template-react-leo/src/workers/worker.js +++ b/create-aleo-app/template-react-leo/src/workers/worker.js @@ -37,7 +37,7 @@ async function deployProgram(program) { keyProvider.useCache(true); // Create a record provider that will be used to find records and transaction data for Aleo programs - const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); + const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); // Use existing account with funds const account = new Account({ @@ -48,7 +48,7 @@ async function deployProgram(program) { // Initialize a program manager to talk to the Aleo network with the configured key and record providers const programManager = new ProgramManager( - "https://vm.aleo.org/api", + "https://api.explorer.aleo.org/v1", keyProvider, recordProvider, ); diff --git a/sdk/README.md b/sdk/README.md index 9eda2c892..1e1a850f6 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -259,12 +259,12 @@ const keyProvider = new AleoKeyProvider(); keyProvider.useCache = true; // Create a record provider that will be used to find records and transaction data for Aleo programs -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager to talk to the Aleo network with the configured key and record providers const programName = "hello_hello.aleo"; -const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider); +const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider); // Provide a key search parameter to find the correct key for the program if they are stored in a memory cache const keySearchParams = { "cacheKey": "hello_hello:hello" }; @@ -325,7 +325,7 @@ const keyProvider = new AleoKeyProvider(); keyProvider.useCache(true); // Create a record provider that will be used to find records and transaction data for Aleo programs -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); // Use existing account with funds const account = new Account({ @@ -335,7 +335,7 @@ const account = new Account({ const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager to talk to the Aleo network with the configured key and record providers -const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider); +const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider); programManager.setAccount(account) // Define an Aleo program to deploy @@ -388,9 +388,9 @@ import * as aleo from "@aleohq/sdk"; await aleo.initThreadPool(); /// The program manager is initialized with a key provider and a record provider -const defaultHost = "https://vm.aleo.org/api"; +const defaultHost = "https://api.explorer.aleo.org/v1"; const keyProvider = new aleo.AleoKeyProvider(); -const recordProvider = new aleo.NetworkRecordProvider(new Account(), "https://vm.aleo.org/api"); +const recordProvider = new aleo.NetworkRecordProvider(new Account(), "https://api.explorer.aleo.org/v1"); const programManager = new aleo.ProgramManager( defaultHost, keyProvider, @@ -767,13 +767,13 @@ import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNe // Create a new NetworkClient, KeyProvider, and RecordProvider const account = Account.from_string({privateKey: "user1PrivateKey"}); -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); const keyProvider = new AleoKeyProvider(); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions const USER_1_ADDRESS = "user1Address"; -const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider); +const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider); programManager.setAccount(account); // Send a private transfer to yourself @@ -808,7 +808,7 @@ assert(public_balance === 0); As shown above, a public balance of any address can be checked with `getMappingValue` function of the `NetworkClient`. ```typescript -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); const USER_1_ADDRESS = "user1Address"; const public_balance = networkClient.getMappingValue("credits.aleo", USER_1_ADDRESS); ``` @@ -897,13 +897,13 @@ import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNe // Create a new NetworkClient, KeyProvider, and RecordProvider const account = Account.from_string({privateKey: "user1PrivateKey"}); -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); const keyProvider = new AleoKeyProvider(); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions const USER_2_ADDRESS = "user2Address"; -const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider); +const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider); programManager.setAccount(account); /// Send private transfer to user 2 @@ -918,12 +918,12 @@ import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNe // Create a new NetworkClient, KeyProvider, and RecordProvider const account = Account.from_string({privateKey: "user2PrivateKey"}); -const networkClient = new AleoNetworkClient("https://vm.aleo.org/api"); +const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1"); const keyProvider = new AleoKeyProvider(); const recordProvider_User2 = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions -const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider); +const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider); programManager.setAccount(account); // Fetch the transaction from the network that user 1 sent diff --git a/sdk/docs/AleoNetworkClient.html b/sdk/docs/AleoNetworkClient.html index a7782bcc4..d98259076 100644 --- a/sdk/docs/AleoNetworkClient.html +++ b/sdk/docs/AleoNetworkClient.html @@ -4,7 +4,7 @@ let local_connection = new AleoNetworkClient("http://localhost:3030"); // Connection to a public beacon node -let public_connection = new AleoNetworkClient("https://vm.aleo.org/api");

Classes

AleoNetworkClient

Methods

(async) findUnspentRecords()

Attempts to find unspent records in the Aleo blockchain for a specified private key
Example
// Find all unspent records
+let public_connection = new AleoNetworkClient("https://api.explorer.aleo.org/v1");

Classes

AleoNetworkClient

Methods

(async) findUnspentRecords()

Attempts to find unspent records in the Aleo blockchain for a specified private key
Example
// Find all unspent records
 const privateKey = "[PRIVATE_KEY]";
 let records = connection.findUnspentRecords(0, undefined, privateKey);
 
diff --git a/sdk/docs/aleo_network_client.ts.html b/sdk/docs/aleo_network_client.ts.html
index d37e94a0b..1d06936d7 100644
--- a/sdk/docs/aleo_network_client.ts.html
+++ b/sdk/docs/aleo_network_client.ts.html
@@ -14,7 +14,7 @@
  * let local_connection = new AleoNetworkClient("http://localhost:3030");
  *
  * // Connection to a public beacon node
- * let public_connection = new AleoNetworkClient("https://vm.aleo.org/api");
+ * let public_connection = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
  */
 export class AleoNetworkClient {
   host: string;
diff --git a/sdk/src/function-key-provider.ts b/sdk/src/function-key-provider.ts
index b647e4b49..7c5601cc0 100644
--- a/sdk/src/function-key-provider.ts
+++ b/sdk/src/function-key-provider.ts
@@ -99,11 +99,11 @@ interface FunctionKeyProvider {
      *
      *
      * const keyProvider = new AleoKeyProvider();
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
      * // Initialize a program manager with the key provider to automatically fetch keys for value transfers
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      *
      * // Keys can also be fetched manually
@@ -120,12 +120,12 @@ interface FunctionKeyProvider {
      *
      * @example
      * // Create a new object which implements the KeyProvider interface
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
      * // Initialize a program manager with the key provider to automatically fetch keys for value transfers
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      *
      * // Keys can also be fetched manually
@@ -271,13 +271,13 @@ class AleoKeyProvider implements FunctionKeyProvider {
      *
      * @example
      * // Create a new object which implements the KeyProvider interface
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      * const AleoProviderParams = new AleoProviderParams("https://testnet3.parameters.aleo.org/transfer_private.");
      *
      * // Initialize a program manager with the key provider to automatically fetch keys for value transfers
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      *
      * // Keys can also be fetched manually using the key provider
@@ -323,7 +323,7 @@ class AleoKeyProvider implements FunctionKeyProvider {
      *
      * @example
      * // Create a new AleoKeyProvider object
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
diff --git a/sdk/src/network-client.ts b/sdk/src/network-client.ts
index cfb0e0768..b3889821c 100644
--- a/sdk/src/network-client.ts
+++ b/sdk/src/network-client.ts
@@ -24,7 +24,7 @@ type ProgramImports = { [key: string]: string | Program };
  * const localNetworkClient = new AleoNetworkClient("http://localhost:3030");
  *
  * // Connection to a public beacon node
- * const publicnetworkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+ * const publicnetworkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
  */
 class AleoNetworkClient {
   host: string;
diff --git a/sdk/src/program-manager.ts b/sdk/src/program-manager.ts
index 4c9d6c678..ac14cc16d 100644
--- a/sdk/src/program-manager.ts
+++ b/sdk/src/program-manager.ts
@@ -17,8 +17,9 @@ import {
     PRIVATE_TRANSFER_TYPES,
     VALID_TRANSFER_TYPES,
     logAndThrow,
-    ProgramManagerBase as WasmProgramManager,
+    ProgramManagerBase as WasmProgramManager, verifyFunctionExecution,
 } from "./index";
+import {Execution} from "@aleohq/wasm/dist/crates/aleo_wasm";
 
 
 
@@ -40,7 +41,7 @@ class ProgramManager {
      */
     constructor(host: string | undefined, keyProvider: FunctionKeyProvider | undefined, recordProvider: RecordProvider | undefined) {
         if (!host) {
-            this.host = "https://vm.aleo.org/api";
+            this.host = "https://api.explorer.aleo.org/v1";
             this.networkClient = new AleoNetworkClient(this.host);
         } else {
             this.host = host;
@@ -107,13 +108,13 @@ class ProgramManager {
      *
      * @example
      * // Create a new NetworkClient, KeyProvider, and RecordProvider
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
      * // 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("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      *
      * // Define a fee in credits
      * const fee = 1.2;
@@ -355,7 +356,7 @@ class ProgramManager {
         console.log("Running program offline")
         console.log("Proving key: ", provingKey);
         console.log("Verifying key: ", verifyingKey);
-        return WasmProgramManager.executeFunctionOffline(executionPrivateKey, program, function_name, inputs, proveExecution, false, imports, provingKey, verifyingKey);
+        return WasmProgramManager.executeFunctionOffline(executionPrivateKey, program, function_name, inputs, proveExecution, false, imports, provingKey, verifyingKey, this.host);
     }
 
     /**
@@ -595,6 +596,25 @@ class ProgramManager {
         return await this.networkClient.submitTransaction(tx);
     }
 
+    /**
+     * Verify a proof of execution from an offline execution
+     *
+     * @param {executionResponse} executionResponse
+     * @returns {boolean} True if the proof is valid, false otherwise
+     */
+    verifyExecution(executionResponse: ExecutionResponse): boolean {
+        try {
+            const execution = executionResponse.getExecution();
+            const function_id = executionResponse.getFunctionId();
+            const program = executionResponse.getProgram();
+            const verifyingKey = executionResponse.getVerifyingKey();
+            return verifyFunctionExecution(execution, function_id, verifyingKey, function_id);
+        } catch(e) {
+            console.warn("The execution was not found in the response, cannot verify the execution");
+            return false;
+        }
+    }
+
     /**
      * Create a program object from a program's source code
      *
diff --git a/sdk/src/record-provider.ts b/sdk/src/record-provider.ts
index 6b59c8768..cc508e851 100644
--- a/sdk/src/record-provider.ts
+++ b/sdk/src/record-provider.ts
@@ -38,7 +38,7 @@ interface RecordProvider {
      *
      * // When the program manager is initialized with the record provider it will be used to find automatically find
      * // fee records and amount records for value transfers so that they do not need to be specified manually
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      */
     findCreditsRecord(microcredits: number, unspent: boolean,  nonces?: string[], searchParameters?: RecordSearchParams): Promise;
@@ -64,7 +64,7 @@ interface RecordProvider {
      *
      * // When the program manager is initialized with the record provider it will be used to find automatically find
      * // fee records and amount records for value transfers so that they do not need to be specified manually
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      */
     findCreditsRecords(microcreditAmounts: number[], unspent: boolean, nonces?: string[], searchParameters?: RecordSearchParams): Promise;
@@ -169,7 +169,7 @@ class NetworkRecordProvider implements RecordProvider {
      *
      * @example
      * // Create a new NetworkRecordProvider
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
@@ -182,7 +182,7 @@ class NetworkRecordProvider implements RecordProvider {
      *
      * // When the program manager is initialized with the record provider it will be used to find automatically find
      * // fee records and amount records for value transfers so that they do not need to be specified manually
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      *
      * */
@@ -228,7 +228,7 @@ class NetworkRecordProvider implements RecordProvider {
      *
      * @example
      * // Create a new NetworkRecordProvider
-     * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+     * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
      * const keyProvider = new AleoKeyProvider();
      * const recordProvider = new NetworkRecordProvider(account, networkClient);
      *
@@ -241,7 +241,7 @@ class NetworkRecordProvider implements RecordProvider {
      *
      * // When the program manager is initialized with the record provider it will be used to find automatically find
      * // fee records and amount records for value transfers so that they do not need to be specified manually
-     * const programManager = new ProgramManager("https://vm.aleo.org/api", keyProvider, recordProvider);
+     * const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", keyProvider, recordProvider);
      * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5);
      */
     async findCreditsRecord(microcredits: number, unspent: boolean, nonces?: string[], searchParameters?: RecordSearchParams): Promise {
@@ -278,7 +278,7 @@ class NetworkRecordProvider implements RecordProvider {
  * const params = new BlockHeightSearch(89995, 99995);
  *
  * // Create a new NetworkRecordProvider
- * const networkClient = new AleoNetworkClient("https://vm.aleo.org/api");
+ * const networkClient = new AleoNetworkClient("https://api.explorer.aleo.org/v1");
  * const keyProvider = new AleoKeyProvider();
  * const recordProvider = new NetworkRecordProvider(account, networkClient);
  *
diff --git a/sdk/src/worker.ts b/sdk/src/worker.ts
index 4de5b192a..ebd287489 100644
--- a/sdk/src/worker.ts
+++ b/sdk/src/worker.ts
@@ -4,7 +4,7 @@ import { expose } from "comlink";
 
 await initThreadPool();
 
-const defaultHost = "https://vm.aleo.org/api";
+const defaultHost = "https://api.explorer.aleo.org/v1";
 const keyProvider = new AleoKeyProvider();
 const programManager = new ProgramManager(
     defaultHost,
diff --git a/wasm/src/programs/execution.rs b/wasm/src/programs/execution.rs
index fa8863ad4..e6899baf8 100644
--- a/wasm/src/programs/execution.rs
+++ b/wasm/src/programs/execution.rs
@@ -75,9 +75,9 @@ pub fn verify_function_execution(
     execution: &Execution,
     verifying_key: &VerifyingKey,
     program: &Program,
-    function_id: String,
+    function_id: &str,
 ) -> Result {
-    let function = IdentifierNative::from_str(&function_id).map_err(|e| e.to_string())?;
+    let function = IdentifierNative::from_str(function_id).map_err(|e| e.to_string())?;
     let program_id = ProgramID::::from_str(&program.id()).unwrap();
     let mut process = ProcessNative::load_web().map_err(|e| e.to_string())?;
     if &program.id() != "credits.aleo" {
@@ -102,13 +102,8 @@ mod tests {
         let verifying_key_bytes = snarkvm_parameters::testnet3::TransferPublicVerifier::load_bytes().unwrap();
         let verifying_key = VerifyingKey::from_bytes(&verifying_key_bytes).unwrap();
         assert!(
-            verify_function_execution(
-                &execution,
-                &verifying_key,
-                &Program::get_credits_program(),
-                "transfer_public".to_string()
-            )
-            .unwrap()
+            verify_function_execution(&execution, &verifying_key, &Program::get_credits_program(), "transfer_public")
+                .unwrap()
         );
     }
 }
diff --git a/wasm/src/programs/manager/deploy.rs b/wasm/src/programs/manager/deploy.rs
index d077d3eef..2e033c510 100644
--- a/wasm/src/programs/manager/deploy.rs
+++ b/wasm/src/programs/manager/deploy.rs
@@ -64,7 +64,7 @@ impl ProgramManager {
         program: &str,
         fee_credits: f64,
         fee_record: Option,
-        url: &str,
+        url: Option,
         imports: Option,
         fee_proving_key: Option,
         fee_verifying_key: Option,
@@ -87,6 +87,7 @@ impl ProgramManager {
         let rng = &mut StdRng::from_entropy();
 
         log("Creating deployment");
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let deployment = process.deploy::(&program, rng).map_err(|err| err.to_string())?;
         if deployment.program().functions().is_empty() {
             return Err("Attempted to create an empty transaction deployment".to_string());
@@ -109,7 +110,7 @@ impl ProgramManager {
             private_key,
             fee_record,
             fee_microcredits,
-            url,
+            node_url,
             fee_proving_key,
             fee_verifying_key,
             deployment_id,
diff --git a/wasm/src/programs/manager/execute.rs b/wasm/src/programs/manager/execute.rs
index d36cae543..d04ec5213 100644
--- a/wasm/src/programs/manager/execute.rs
+++ b/wasm/src/programs/manager/execute.rs
@@ -65,8 +65,10 @@ impl ProgramManager {
         imports: Option,
         proving_key: Option,
         verifying_key: Option,
+        url: Option,
     ) -> Result {
         log(&format!("Executing local function: {function}"));
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let inputs = inputs.to_vec();
         let rng = &mut StdRng::from_entropy();
 
@@ -88,20 +90,24 @@ impl ProgramManager {
             rng
         );
 
-        let process_native = if cache { Some(process_native) } else { None };
-
-        if prove_execution {
+        let mut execution_response = if prove_execution {
             log("Preparing inclusion proofs for execution");
-            let query = QueryNative::from("https://vm.aleo.org/api");
+            let query = QueryNative::from(node_url);
             trace.prepare_async(query).await.map_err(|err| err.to_string())?;
 
             log("Proving execution");
             let locator = program_native.id().to_string().add("/").add(function);
             let execution = trace.prove_execution::(&locator, rng).map_err(|e| e.to_string())?;
-            Ok(ExecutionResponse::from((response, execution, process_native)))
+            ExecutionResponse::new(Some(execution), function, response, process, program)?
         } else {
-            Ok(ExecutionResponse::from((response, process_native)))
+            ExecutionResponse::new(None, function, response, process, program)?
+        };
+
+        if cache {
+            execution_response.add_proving_key(process, function, program_native.id())?;
         }
+
+        Ok(execution_response)
     }
 
     /// Execute Aleo function and create an Aleo execution transaction
@@ -134,7 +140,7 @@ impl ProgramManager {
         inputs: Array,
         fee_credits: f64,
         fee_record: Option,
-        url: &str,
+        url: Option,
         imports: Option,
         proving_key: Option,
         verifying_key: Option,
@@ -146,9 +152,9 @@ impl ProgramManager {
             Some(fee_record) => Self::validate_amount(fee_credits, fee_record, true)?,
             None => (fee_credits * 1_000_000.0) as u64,
         };
-
         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(DEFAULT_URL);
 
         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())?;
@@ -168,7 +174,7 @@ impl ProgramManager {
         );
 
         log("Preparing inclusion proofs for execution");
-        let query = QueryNative::from(url);
+        let query = QueryNative::from(node_url);
         trace.prepare_async(query).await.map_err(|err| err.to_string())?;
 
         log("Proving execution");
@@ -185,7 +191,7 @@ impl ProgramManager {
             private_key,
             fee_record,
             fee_microcredits,
-            url,
+            node_url,
             fee_proving_key,
             fee_verifying_key,
             execution_id,
@@ -224,7 +230,7 @@ impl ProgramManager {
         program: &str,
         function: &str,
         inputs: Array,
-        url: &str,
+        url: Option,
         imports: Option,
         proving_key: Option,
         verifying_key: Option,
@@ -255,9 +261,10 @@ impl ProgramManager {
         );
 
         // Execute the program
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let program = ProgramNative::from_str(program).map_err(|err| err.to_string())?;
         let locator = program.id().to_string().add("/").add(function);
-        let query = QueryNative::from(url);
+        let query = QueryNative::from(node_url);
         trace.prepare_async(query).await.map_err(|err| err.to_string())?;
         let execution = trace.prove_execution::(&locator, rng).map_err(|e| e.to_string())?;
 
diff --git a/wasm/src/programs/manager/join.rs b/wasm/src/programs/manager/join.rs
index af1adea72..350f8a8d7 100644
--- a/wasm/src/programs/manager/join.rs
+++ b/wasm/src/programs/manager/join.rs
@@ -55,7 +55,7 @@ impl ProgramManager {
         record_2: RecordPlaintext,
         fee_credits: f64,
         fee_record: Option,
-        url: &str,
+        url: Option,
         join_proving_key: Option,
         join_verifying_key: Option,
         fee_proving_key: Option,
@@ -69,6 +69,7 @@ impl ProgramManager {
         let rng = &mut StdRng::from_entropy();
 
         log("Setup program and inputs");
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let program = ProgramNative::credits().unwrap().to_string();
         let inputs = Array::new_with_length(2);
         inputs.set(0u32, wasm_bindgen::JsValue::from_str(&record_1.to_string()));
@@ -107,7 +108,7 @@ impl ProgramManager {
         );
 
         log("Preparing inclusion proof for the join execution");
-        let query = QueryNative::from(url);
+        let query = QueryNative::from(node_url);
         trace.prepare_async(query).await.map_err(|err| err.to_string())?;
 
         log("Proving the join execution");
@@ -123,7 +124,7 @@ impl ProgramManager {
             private_key,
             fee_record,
             fee_microcredits,
-            url,
+            node_url,
             fee_proving_key,
             fee_verifying_key,
             execution_id,
diff --git a/wasm/src/programs/manager/mod.rs b/wasm/src/programs/manager/mod.rs
index 6ff96c4cf..5eb89dfee 100644
--- a/wasm/src/programs/manager/mod.rs
+++ b/wasm/src/programs/manager/mod.rs
@@ -29,6 +29,8 @@ pub use split::*;
 pub mod transfer;
 pub use transfer::*;
 
+const DEFAULT_URL: &str = "https://api.explorer.aleo.org/v1";
+
 use crate::{
     types::{
         cost_in_microcredits,
@@ -88,7 +90,6 @@ impl ProgramManager {
         inputs: js_sys::Array,
         imports: Option,
     ) -> Result {
-        let program_id = ProgramNative::from_str(program).map_err(|e| e.to_string())?.id().to_string();
         ProgramManager::execute_function_offline(
             private_key,
             program,
@@ -99,9 +100,10 @@ impl ProgramManager {
             imports,
             None,
             None,
+            None,
         )
         .await?
-        .get_keys(&program_id, function_id)
+        .get_keys()
     }
 
     /// Check if a process contains a keypair for a specific function
diff --git a/wasm/src/programs/manager/split.rs b/wasm/src/programs/manager/split.rs
index e89fca776..35eb67da5 100644
--- a/wasm/src/programs/manager/split.rs
+++ b/wasm/src/programs/manager/split.rs
@@ -48,7 +48,7 @@ impl ProgramManager {
         private_key: &PrivateKey,
         split_amount: f64,
         amount_record: RecordPlaintext,
-        url: &str,
+        url: Option,
         split_proving_key: Option,
         split_verifying_key: Option,
     ) -> Result {
@@ -56,6 +56,7 @@ impl ProgramManager {
         let amount_microcredits = Self::validate_amount(split_amount, &amount_record, false)?;
 
         log("Setup the program and inputs");
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let program = ProgramNative::credits().unwrap().to_string();
         let inputs = Array::new_with_length(2u32);
         inputs.set(0u32, wasm_bindgen::JsValue::from_str(&amount_record.to_string()));
@@ -78,7 +79,7 @@ impl ProgramManager {
         );
 
         log("Preparing the inclusion proof for the split execution");
-        let query = QueryNative::from(url);
+        let query = QueryNative::from(node_url);
         trace.prepare_async(query).await.map_err(|err| err.to_string())?;
 
         log("Proving the split execution");
diff --git a/wasm/src/programs/manager/transfer.rs b/wasm/src/programs/manager/transfer.rs
index c590f96c1..214b4352e 100644
--- a/wasm/src/programs/manager/transfer.rs
+++ b/wasm/src/programs/manager/transfer.rs
@@ -58,7 +58,7 @@ impl ProgramManager {
         amount_record: Option,
         fee_credits: f64,
         fee_record: Option,
-        url: &str,
+        url: Option,
         transfer_proving_key: Option,
         transfer_verifying_key: Option,
         fee_proving_key: Option,
@@ -75,6 +75,7 @@ impl ProgramManager {
         };
 
         log("Setup the program and inputs");
+        let node_url = url.as_deref().unwrap_or(DEFAULT_URL);
         let program = ProgramNative::credits().unwrap().to_string();
         let rng = &mut StdRng::from_entropy();
 
@@ -149,7 +150,7 @@ impl ProgramManager {
         );
 
         log("Preparing the inclusion proof for the transfer execution");
-        let query = QueryNative::from(url);
+        let query = QueryNative::from(node_url);
         trace.prepare_async(query).await.map_err(|err| err.to_string())?;
 
         log("Proving the transfer execution");
@@ -166,7 +167,7 @@ impl ProgramManager {
             private_key,
             fee_record,
             fee_microcredits,
-            url,
+            node_url,
             fee_proving_key,
             fee_verifying_key,
             execution_id,
diff --git a/wasm/src/programs/response.rs b/wasm/src/programs/response.rs
index de9a3b4dd..92008c586 100644
--- a/wasm/src/programs/response.rs
+++ b/wasm/src/programs/response.rs
@@ -14,10 +14,19 @@
 // You should have received a copy of the GNU General Public License
 // along with the Aleo SDK library. If not, see .
 
-use crate::types::{ExecutionNative, ProcessNative, ResponseNative};
+use crate::types::{
+    ExecutionNative,
+    IdentifierNative,
+    ProcessNative,
+    ProgramIDNative,
+    ProgramNative,
+    ProvingKeyNative,
+    ResponseNative,
+    VerifyingKeyNative,
+};
 
-use crate::{Execution, KeyPair};
-use std::ops::Deref;
+use crate::{Execution, KeyPair, Program, ProvingKey, VerifyingKey};
+use std::{ops::Deref, str::FromStr};
 use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
 
 /// Webassembly Representation of an Aleo function execution response
@@ -26,13 +35,50 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
 /// retrieving the outputs of the function execution.
 #[wasm_bindgen]
 pub struct ExecutionResponse {
+    execution: Option,
+    function_id: IdentifierNative,
     response: ResponseNative,
-    execution: Option,
-    process: Option,
+    program: ProgramNative,
+    proving_key: Option,
+    verifying_key: VerifyingKeyNative,
 }
 
 #[wasm_bindgen]
 impl ExecutionResponse {
+    pub(crate) fn new(
+        execution: Option,
+        function_id: &str,
+        response: ResponseNative,
+        process: &ProcessNative,
+        program: &str,
+    ) -> Result {
+        let program = ProgramNative::from_str(program).map_err(|e| e.to_string())?;
+        let verifying_key = process
+            .get_verifying_key(program.id(), function_id)
+            .map_err(|_| format!("Could not find verifying key for {:?}/{:?}", program.id(), function_id))?;
+        Ok(Self {
+            execution,
+            response,
+            function_id: IdentifierNative::from_str(function_id).map_err(|e| e.to_string())?,
+            program,
+            proving_key: None,
+            verifying_key,
+        })
+    }
+
+    pub(crate) fn add_proving_key(
+        &mut self,
+        process: &ProcessNative,
+        function_id: &str,
+        program_id: &ProgramIDNative,
+    ) -> Result<(), String> {
+        let proving_key = process
+            .get_proving_key(program_id, function_id)
+            .map_err(|_| format!("Could not find proving key for {:?}/{:?}", program_id, function_id))?;
+        self.proving_key = Some(proving_key);
+        Ok(())
+    }
+
     /// Get the outputs of the executed function
     ///
     /// @returns {Array} Array of strings representing the outputs of the function
@@ -45,50 +91,59 @@ impl ExecutionResponse {
         array
     }
 
-    /// Returns the execution object if present, null if otherwise. Please note that this function
-    /// removes the WebAssembly object from the response object and will return null if called a
-    /// second time.
+    /// Returns the execution object if present, null if otherwise.
     ///
-    /// @returns {Execution} The execution object if present, null if otherwise
+    /// @returns {Execution | undefined} The execution object if present, null if otherwise
     #[wasm_bindgen(js_name = "getExecution")]
-    pub fn get_execution(&mut self) -> Option {
-        self.execution.take()
+    pub fn get_execution(&self) -> Option {
+        self.execution.clone().map(Execution::from)
     }
 
     /// Returns the program keys if present
     #[wasm_bindgen(js_name = "getKeys")]
-    pub fn get_keys(&self, program_id: &str, function_name: &str) -> Result {
-        if let Some(process) = &self.process {
-            let proving_key = process.get_proving_key(program_id, function_name).map_err(|e| e.to_string())?;
-            let verifying_key = process.get_verifying_key(program_id, function_name).map_err(|e| e.to_string())?;
-            return Ok(KeyPair::from((proving_key, verifying_key)));
+    pub fn get_keys(&mut self) -> Result {
+        if let Some(proving_key) = self.proving_key.take() {
+            Ok(KeyPair::new(ProvingKey::from(proving_key), VerifyingKey::from(self.verifying_key.clone())))
+        } else {
+            Err("No proving key found".to_string())
         }
-        Err(format!("Could not find {program_id}:{function_name}"))
     }
-}
 
-impl Deref for ExecutionResponse {
-    type Target = ResponseNative;
+    /// Returns the proving_key if the proving key was cached in the Execution response.
+    /// Note the proving key is removed from the response object after the first call to this
+    /// function. Subsequent calls will return null.
+    ///
+    /// @returns {ProvingKey | undefined} The proving key
+    #[wasm_bindgen(js_name = "getProvingKey")]
+    pub fn get_proving_key(&mut self) -> Option {
+        self.proving_key.take().map(ProvingKey::from)
+    }
 
-    fn deref(&self) -> &Self::Target {
-        &self.response
+    /// Returns the verifying_key associated with the program
+    ///
+    /// @returns {VerifyingKey} The verifying key
+    #[wasm_bindgen(js_name = "getVerifyingKey")]
+    pub fn get_verifying_key(&self) -> VerifyingKey {
+        VerifyingKey::from(self.verifying_key.clone())
     }
-}
 
-impl From<(ResponseNative, Option)> for ExecutionResponse {
-    fn from((response, process): (ResponseNative, Option)) -> Self {
-        Self { response, execution: None, process }
+    /// Returns the function identifier
+    #[wasm_bindgen(js_name = "getFunctionId")]
+    pub fn get_function_id(&self) -> String {
+        format!("{:?}", self.function_id)
     }
-}
 
-impl From<(ResponseNative, ExecutionNative, Option)> for ExecutionResponse {
-    fn from((response, execution, process): (ResponseNative, ExecutionNative, Option)) -> Self {
-        Self { response, execution: Some(Execution::from(execution)), process }
+    /// Returns the program
+    #[wasm_bindgen(js_name = "getProgram")]
+    pub fn get_program(&self) -> Program {
+        Program::from(self.program.clone())
     }
 }
 
-impl From for ResponseNative {
-    fn from(response: ExecutionResponse) -> Self {
-        response.response
+impl Deref for ExecutionResponse {
+    type Target = ResponseNative;
+
+    fn deref(&self) -> &Self::Target {
+        &self.response
     }
 }
diff --git a/wasm/tests/offchain.rs b/wasm/tests/offchain.rs
index e74af29ef..5aca2b6a0 100644
--- a/wasm/tests/offchain.rs
+++ b/wasm/tests/offchain.rs
@@ -118,7 +118,7 @@ async fn test_key_synthesis() {
     let inputs = Array::new();
     inputs.set(0u32, JsValue::from_str(RECORD));
     inputs.set(1u32, JsValue::from_str("5u64"));
-    let result = ProgramManager::execute_function_offline(
+    let mut result = ProgramManager::execute_function_offline(
         &PrivateKey::from_string("APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6").unwrap(),
         &credits,
         "split",
@@ -128,11 +128,12 @@ async fn test_key_synthesis() {
         None,
         Some(retrieved_proving_key.clone()),
         Some(retreived_verifying_key.clone()),
+        None,
     )
     .await
     .unwrap();
 
-    let mut keys = result.get_keys("credits.aleo", "split").unwrap();
+    let mut keys = result.get_keys().unwrap();
     let proving_key = keys.proving_key().unwrap();
     let verifying_key = keys.verifying_key().unwrap();
     assert_eq!(proving_key, retrieved_proving_key);
@@ -156,7 +157,7 @@ async fn test_fee_validation() {
         inputs,
         100.0,
         Some(fee_record.clone()),
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -172,7 +173,7 @@ async fn test_fee_validation() {
         &Program::get_credits_program().to_string(),
         100.0,
         Some(fee_record.clone()),
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -189,7 +190,7 @@ async fn test_fee_validation() {
         Some(fee_record.clone()),
         0.9,
         Some(fee_record.clone()),
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -206,7 +207,7 @@ async fn test_fee_validation() {
         Some(fee_record.clone()),
         100.00,
         Some(fee_record.clone()),
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -222,7 +223,7 @@ async fn test_fee_validation() {
         fee_record.clone(),
         100.00,
         Some(fee_record.clone()),
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -257,7 +258,7 @@ async fn test_fee_estimation() {
         FINALIZE,
         "integer_key_mapping_update",
         inputs,
-        "https://vm.aleo.org/api",
+        Some("https://api.explorer.aleo.org/v1".to_string()),
         None,
         None,
         None,
@@ -296,6 +297,7 @@ async fn test_import_resolution() {
         Some(imports),
         None,
         None,
+        None,
     )
     .await
     .unwrap();
diff --git a/website/src/tabs/develop/execute/LoadProgram.jsx b/website/src/tabs/develop/execute/LoadProgram.jsx
index 52e20c778..346cd9caf 100644
--- a/website/src/tabs/develop/execute/LoadProgram.jsx
+++ b/website/src/tabs/develop/execute/LoadProgram.jsx
@@ -21,7 +21,7 @@ export const LoadProgram = ({ onResponse }) => {
         }
 
         setIsLoading(true);
-        const url = `https://vm.aleo.org/api/testnet3/program/${value}`;
+        const url = `https://api.explorer.aleo.org/v1/testnet3/program/${value}`;
 
         axios
             .get(url)

From b43a2d30318ab76240497266dc1f63ba1e6db9f7 Mon Sep 17 00:00:00 2001
From: Michael Turner 
Date: Thu, 26 Oct 2023 16:47:55 +0200
Subject: [PATCH 2/3] Update to SnarkVM 0.16.6, add ProgramManager tests, add
 Deployment transaction searches

---
 sdk/package.json                  |   2 +-
 sdk/src/network-client.ts         |  45 ++++++-
 sdk/src/program-manager.ts        |   2 +-
 sdk/tests/network-client.test.ts  |  60 +++++----
 sdk/tests/program-manager.test.ts |  17 +++
 wasm/Cargo.lock                   | 208 +++++++++++++++---------------
 wasm/Cargo.toml                   |  18 +--
 wasm/src/programs/response.rs     |   1 +
 8 files changed, 207 insertions(+), 146 deletions(-)
 create mode 100644 sdk/tests/program-manager.test.ts

diff --git a/sdk/package.json b/sdk/package.json
index 7fca67820..9ada1d839 100644
--- a/sdk/package.json
+++ b/sdk/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@aleohq/sdk",
-  "version": "0.6.3",
+  "version": "0.6.5",
   "description": "A Software Development Kit (SDK) for Zero-Knowledge Transactions",
   "collaborators": [
     "The Aleo Team "
diff --git a/sdk/src/network-client.ts b/sdk/src/network-client.ts
index b3889821c..1420c9bf3 100644
--- a/sdk/src/network-client.ts
+++ b/sdk/src/network-client.ts
@@ -307,6 +307,39 @@ class AleoNetworkClient {
     }
   }
 
+  /**
+   * Returns the deployment transaction id associated with the specified program
+   *
+   * @param {Program | string} program
+   * @returns {Transaction | Error}
+   */
+  async getDeploymentTransactionIDForProgram(program: Program | string): Promise {
+    if (program instanceof Program) {
+      program = program.toString();
+    }
+    try {
+      const id = await this.fetchData("/find/transactionID/deployment/" + program);
+      return id.replace("\"", "")
+    } catch (error) {
+      throw new Error("Error fetching deployment transaction for program.");
+    }
+  }
+
+  /**
+   * Returns the deployment transaction associated with a specified program
+   *
+   * @param {Program | string} program
+   * @returns {Transaction | Error}
+   */
+  async getDeploymentTransactionForProgram(program: Program | string): Promise {
+    try {
+      const transaction_id = await this.getDeploymentTransactionIDForProgram(program);
+      return await this.getTransaction(transaction_id);
+    } catch (error) {
+      throw new Error("Error fetching deployment transaction for program.");
+    }
+  }
+
   /**
    * Returns the contents of the latest block
    *
@@ -322,16 +355,15 @@ class AleoNetworkClient {
   }
 
   /**
-   * Returns the hash of the last published block
+   * Returns the latest committee
    *
-   * @example
-   * const latestHash = networkClient.getLatestHash();
+   * @returns {Promise} A javascript object containing the latest committee
    */
-  async getLatestHash(): Promise {
+  async getLatestCommittee(): Promise {
     try {
-      return await this.fetchData("/latest/hash");
+      return await this.fetchData("/committee/latest");
     } catch (error) {
-      throw new Error("Error fetching latest hash.");
+      throw new Error("Error fetching latest block.");
     }
   }
 
@@ -538,6 +570,7 @@ class AleoNetworkClient {
     } catch (error) {
       throw new Error("Error fetching transaction.");
     }
+
   }
 
   /**
diff --git a/sdk/src/program-manager.ts b/sdk/src/program-manager.ts
index ac14cc16d..b33d9fa06 100644
--- a/sdk/src/program-manager.ts
+++ b/sdk/src/program-manager.ts
@@ -608,7 +608,7 @@ class ProgramManager {
             const function_id = executionResponse.getFunctionId();
             const program = executionResponse.getProgram();
             const verifyingKey = executionResponse.getVerifyingKey();
-            return verifyFunctionExecution(execution, function_id, verifyingKey, function_id);
+            return verifyFunctionExecution(execution, verifyingKey, program, function_id);
         } catch(e) {
             console.warn("The execution was not found in the response, cannot verify the execution");
             return false;
diff --git a/sdk/tests/network-client.test.ts b/sdk/tests/network-client.test.ts
index e9a54891e..d13329b13 100644
--- a/sdk/tests/network-client.test.ts
+++ b/sdk/tests/network-client.test.ts
@@ -45,6 +45,20 @@ describe('NodeConnection', () => {
         }, 60000);
     });
 
+    describe('getDeploymentTransactionForProgram', () => {
+        it('should return a Transaction object', async () => {
+            const transaction = await connection.getDeploymentTransactionForProgram('hello_hello.aleo');
+            expect((transaction as Transaction).type).toBe("deploy");
+        }, 60000);
+    });
+
+    describe('getDeploymentTransactionIDForProgram', () => {
+        it('should return a Transaction object', async () => {
+            const transaction = await connection.getDeploymentTransactionIDForProgram('hello_hello.aleo');
+            expect(typeof transaction).toBe('string');
+        }, 60000);
+    });
+
     describe('getProgram', () => {
         it('should return a string', async () => {
             const program = await connection.getProgram('credits.aleo');
@@ -64,10 +78,10 @@ describe('NodeConnection', () => {
         }, 60000);
     });
 
-    describe('getLatestHash', () => {
+    describe('getLatestCommittee', () => {
         it('should return a string', async () => {
-            const latestHash = await connection.getLatestHash();
-            expect(typeof latestHash).toBe('string');
+            const latestCommittee = await connection.getLatestCommittee();
+            expect(typeof latestCommittee).toBe('object');
         }, 60000);
     });
 
@@ -78,7 +92,6 @@ describe('NodeConnection', () => {
         }, 60000);
     });
 
-
     describe('getStateRoot', () => {
         it('should return a string', async () => {
             const stateRoot = await connection.getStateRoot();
@@ -91,10 +104,6 @@ describe('NodeConnection', () => {
             const transaction = await connection.getTransaction('at1u833jaha7gtqk7vx0yczcg2njds2tj52lyg54c7zyylgfjvc4vpqn8gqqx');
             expect((transaction as Transaction).type).toBe("deploy");
         }, 60000);
-
-        it('should throw an error if the request fails', async () => {
-            await expect(connection.getTransaction('nonexistentid')).rejects.toThrow('Error fetching transaction.');
-        }, 60000);
     });
 
     describe('getTransactions', () => {
@@ -109,23 +118,6 @@ describe('NodeConnection', () => {
         }, 60000);
     });
 
-    describe('findUnspentRecords', () => {
-        it('should fail if block heights or private keys are incorrectly specified', async () => {
-            await expect(connection.findUnspentRecords(5, 0, beaconPrivateKeyString, undefined, undefined, [])).rejects.toThrow();
-            await expect(connection.findUnspentRecords(-5, 5, beaconPrivateKeyString, undefined, undefined, [])).rejects.toThrow();
-            await expect(connection.findUnspentRecords(0, 5, "definitelynotaprivatekey", undefined, undefined, [])).rejects.toThrow();
-            await expect(connection.findUnspentRecords(0, 5, undefined, undefined, undefined, [])).rejects.toThrow();
-        }, 60000);
-
-        it('should search a range correctly and not find records where none exist', async () => {
-            const records = await connection.findUnspentRecords(0, 204, beaconPrivateKeyString, undefined, undefined, []);
-            expect(Array.isArray(records)).toBe(true);
-            if (!(records instanceof Error)) {
-                expect(records.length).toBe(0);
-            }
-        }, 90000);
-    });
-
     describe('getProgramImports', () => {
         it('should return the correct program import names', async () => {
             const importNames = await connection.getProgramImportNames("aleoswap05.aleo");
@@ -166,6 +158,24 @@ describe('NodeConnection', () => {
             expect(imports).toEqual(expectedImports);
         }, 60000);
     });
+
+    describe('findUnspentRecords', () => {
+        it('should fail if block heights or private keys are incorrectly specified', async () => {
+            await expect(connection.findUnspentRecords(5, 0, beaconPrivateKeyString, undefined, undefined, [])).rejects.toThrow();
+            await expect(connection.findUnspentRecords(-5, 5, beaconPrivateKeyString, undefined, undefined, [])).rejects.toThrow();
+            await expect(connection.findUnspentRecords(0, 5, "definitelynotaprivatekey", undefined, undefined, [])).rejects.toThrow();
+            await expect(connection.findUnspentRecords(0, 5, undefined, undefined, undefined, [])).rejects.toThrow();
+        }, 60000);
+
+        it('should search a range correctly and not find records where none exist', async () => {
+            const records = await connection.findUnspentRecords(0, 204, beaconPrivateKeyString, undefined, undefined, []);
+            expect(Array.isArray(records)).toBe(true);
+            if (!(records instanceof Error)) {
+                expect(records.length).toBe(0);
+            }
+        }, 90000);
+    });
+
     describe('Mappings', () => {
         it('should find program mappings and read mappings', async () => {
             const mappings = await connection.getProgramMappingNames("credits.aleo");
diff --git a/sdk/tests/program-manager.test.ts b/sdk/tests/program-manager.test.ts
new file mode 100644
index 000000000..cb7e08069
--- /dev/null
+++ b/sdk/tests/program-manager.test.ts
@@ -0,0 +1,17 @@
+import {jest} from '@jest/globals'
+import {beaconPrivateKeyString, helloProgram} from "./data/account-data";
+import { Account, ExecutionResponse, ProgramManager } from "../src/node";
+jest.retryTimes(3);
+
+describe('Program Manager', () => {
+    const programManager = new ProgramManager("https://api.explorer.aleo.org/v1", undefined, undefined);
+    programManager.setAccount(new Account());
+
+    describe('Execute offline', () => {
+        it('Program manager should execute offline and verify the resulting proof correctly', async () => {
+            const execution_result = await programManager.executeOffline(helloProgram, "hello", ["5u32", "5u32"], true, undefined, undefined, undefined, undefined, undefined)
+            expect(execution_result.getOutputs()[0]).toEqual("10u32");
+            programManager.verifyExecution(execution_result);
+        }, 420000);
+    });
+});
\ No newline at end of file
diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock
index b29ffcaad..1229d41ef 100644
--- a/wasm/Cargo.lock
+++ b/wasm/Cargo.lock
@@ -1614,9 +1614,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-algorithms"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f83ddba1b92dcfef4b01d7bc27d83b66ebed671db7d7ea2ceb9f1837fa3dcbf"
+checksum = "2a3cd0ead41ef019e92b5bbcfa8226572291afb67fdee503239f8e9f7309fbb4"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -1646,9 +1646,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96d2504479eee16718b0f1118d804f294bd228abf09f6e45e0cae2de1bea94cf"
+checksum = "2077a5e63c449fe3983050292f5e53f8865186b97f0543f1e7b1e6af8cdfac82"
 dependencies = [
  "snarkvm-circuit-account",
  "snarkvm-circuit-algorithms",
@@ -1661,9 +1661,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-account"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e85a791132c4588fd1dd0178372abfb4679e19b727d5473149ac0720d1f0646"
+checksum = "573576d631120789f288a354b4a1756f1a71d9b3108ae5d5c35a74e77772e546"
 dependencies = [
  "snarkvm-circuit-algorithms",
  "snarkvm-circuit-network",
@@ -1673,9 +1673,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-algorithms"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e4485e73705f94ce6025b02d29f27dbcdd2d7737fcb0b1a8934a17ca06a59a4"
+checksum = "c99806eeadf6895c031cb180362c834829e6acc17d2af9b3a0d52a61f0d02485"
 dependencies = [
  "snarkvm-circuit-types",
  "snarkvm-console-algorithms",
@@ -1684,9 +1684,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-collections"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7affff6b1be13067c042a1ae5a88b07c4436c4e0c6677a98c0abfd3f3d20da27"
+checksum = "8e77b30e1e7996a403029bdab6f3cc29c0f25c3b1e7fcfc3b3aa15535367d3b0"
 dependencies = [
  "snarkvm-circuit-algorithms",
  "snarkvm-circuit-types",
@@ -1695,9 +1695,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-environment"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "119f99621408d4c301ebd33891dfab60d374d1d234b6f166f28de3c7765d0ec1"
+checksum = "223cc48dd71f4bfd0057e2495f20e29a37e56927292747b34253d77dba9242fa"
 dependencies = [
  "indexmap 2.0.2",
  "itertools",
@@ -1714,15 +1714,15 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-environment-witness"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95ed30aec3790bb687516e0fd07a711beb48dc7c58ad290b54e8f2ca8669ddda"
+checksum = "f71c2eeab0b10c64a495bee0afbc1abf27c6f6c3b43ec7ee56c828cb052a946b"
 
 [[package]]
 name = "snarkvm-circuit-network"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "831f4f76bda79240dc9882b13198a073bde89fb5dc4dbde8e757dd4120ac3463"
+checksum = "b8771980ab198c49d46c7317e0745fcb5230094b612d9edf837057e1891c60af"
 dependencies = [
  "snarkvm-circuit-algorithms",
  "snarkvm-circuit-collections",
@@ -1732,9 +1732,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-program"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff21c8ef94092350024816cbd4a7c8c5794dc9ca19ae87cb2870e03d940b1064"
+checksum = "c6f5cf618d21e4086817e2c6877096612e5507cb8174886ed91399d44eecc584"
 dependencies = [
  "paste",
  "snarkvm-circuit-account",
@@ -1748,9 +1748,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a49dda0564fb337a715a156cb2c9adbbfe7b753805d25db79a9e0ab867fd859f"
+checksum = "6eb78d6fa5fc1d1f1deed7cca1ef3b77dceedfe764a30d54d111769d8dd8806d"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-address",
@@ -1764,9 +1764,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-address"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "476d7cfe33d3364c5dcee524d66e4b71f92f6b5615c2ae54249fa9960a3e8723"
+checksum = "902177b15907ff6599d82fb8d432aa7cbbf8d84ab8dcad0598b3d73c6202f1eb"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1778,9 +1778,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-boolean"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7819b6cf75c64f533c0031a7caa04e4dd8f11b8b81d858b1f36eb2c54e5a020b"
+checksum = "e58f2f52369b70af7dfd316bc89485aa0d9561b9824cdea89ea24acec40c644f"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-console-types-boolean",
@@ -1788,9 +1788,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-field"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1699d379214be5f1090062f370a0cc956dc91797231ce0489ee457fcc7c1ad3"
+checksum = "74eaae0b7624bd73dde9def62ffdb6ee30aaf64f211c8d2155fca3bc441bbe51"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1799,9 +1799,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-group"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f34b8e6e90bcdf9bbdb72f9e74f8173511e63162584d226dd80da312a76aaa37"
+checksum = "de9098c2f9ff6714b5628f1210dab2f27019519213d4f5d6329b640875433b89"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1812,9 +1812,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-integers"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bedf9a68dcee59927ba95f9132dc8d20b575670f60f8d98f5a9a990a8e5ad77"
+checksum = "2f210df5327b3c4f77fd854dc0093d637f406cecf7ab055086b2c4344a85dd80"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1825,9 +1825,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-scalar"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b35492a96e5c6a928d926f7ca4b4c44177b7b6fbdf21753e21262f1341f4373"
+checksum = "096791aca4d8deedc27f8f7c960eb9a235d3376d0920fab5fa8f50c53d6c0210"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1837,9 +1837,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-circuit-types-string"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d275d4364a8cc13ae05f74d61558ed41eb6f115f50e2327f7dc6156602f7331a"
+checksum = "e58efa0189da37ae6e2b2d7680a31fe7ea89d8ffa19452f8e2710ada9e25bacc"
 dependencies = [
  "snarkvm-circuit-environment",
  "snarkvm-circuit-types-boolean",
@@ -1850,9 +1850,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7499c43be86be04f899f412681d51d9162c4fda1e3f8db38623cd45a1e4f3794"
+checksum = "b7113a092a6bcf026823e93c17797c30c5cb94ded77becfed6af9c7840113d87"
 dependencies = [
  "snarkvm-console-account",
  "snarkvm-console-algorithms",
@@ -1864,9 +1864,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-account"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e9e1f9f492eba725ec3ee2fc8ba59b842ee05053c3320a3f9eb57e89c08fe11"
+checksum = "673448b3679feb3c70a533fe04a75567a39fb8209c14d48e092cce11cfbddcbf"
 dependencies = [
  "bs58",
  "snarkvm-console-network",
@@ -1876,9 +1876,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-algorithms"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b43568931447b9607abb65237e5273a62fa5bdbb3ef2f4762d0261942c203ee6"
+checksum = "2f47b413a30d3f7117b8ee8e2be935820606fc3c59725588b301976824ae2b66"
 dependencies = [
  "blake2s_simd",
  "smallvec",
@@ -1890,9 +1890,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-collections"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34c51ea7d841dc5e49e079dd10dd1be224babbdd95672cb866299215b4d1c4c3"
+checksum = "ac544da5cf5c11fde6774de793649528ce73c92964b6d102f94662faae156b26"
 dependencies = [
  "aleo-std",
  "rayon",
@@ -1902,9 +1902,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-network"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec16c87e13f315800e7a02f0121705c8181bb1143dfccb21155590e66d71ecc"
+checksum = "b175dc188dd13ab30f381ba3041e2b8a1623d34027b2c416620899e9ec42d097"
 dependencies = [
  "anyhow",
  "indexmap 2.0.2",
@@ -1926,9 +1926,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-network-environment"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d259da537db770068e081fe4b99bd98f24bd322dd76d8d52ba3062a47149ff21"
+checksum = "b870845575c2683018590279742f17e2ea604fb2f0065e77b787f920b90b771f"
 dependencies = [
  "anyhow",
  "bech32",
@@ -1945,9 +1945,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-program"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b08ab52ad281f4ab737635bf05098709070d5be238b3ee79158d5c0de71f7f82"
+checksum = "1a5c9316868b05568df87099034d6b49cd7214d6a57f13de169bfe5be86ba04d"
 dependencies = [
  "enum_index",
  "enum_index_derive",
@@ -1967,9 +1967,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5023b512da7c0ec997b22a11a046b0caf66a9c06dac96e63b2eab38e3254ff47"
+checksum = "0ad39ef257f8f313a6f3e45eb74fbde35d85571810e31594e389b091270a7ebe"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-address",
@@ -1983,9 +1983,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-address"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "256ef9f8bdab292d7791b37f5e5fba4c3bf5da5c0a71c85aa19981bcf3a34298"
+checksum = "4185b4743912dbd46971e15b3d9af78a9a897a9bbc78f8735380f97bd6bc4aa9"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -1995,18 +1995,18 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-boolean"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c2851ad8bb4bc84561529c4601924ff4dbd6460a46eb99ad433cf1a78097eb"
+checksum = "19e802cbc98655d27cebe26bfd32c9a5de809f4846c6a87f13dfd430117e9b34"
 dependencies = [
  "snarkvm-console-network-environment",
 ]
 
 [[package]]
 name = "snarkvm-console-types-field"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54fedf4a92fe01eb9d2a886c9de9d52ee96f60f04031976bcea00f3c9ba895e2"
+checksum = "3d549071da6521f5fd852ce9635efd86973b7f039713a4264244e006ea2c613f"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -2015,9 +2015,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-group"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6701a6f9804366c63e1176174823f2c48a1bc2a94164bc1731cb0fb5f64dda4"
+checksum = "f3215e7a711aecf339e84657d0d3c0cc504cd1e2194bbfd1fcbf5fac5670dcce"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -2027,9 +2027,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-integers"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5dabb0f536fc7b53444c20d68b77369f33eb37b4153b947e25ec9d1a57c051d"
+checksum = "0bd543873da9ba4964878ef859cca25e4c590eb23c5caee21c231532e1f42056"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -2039,9 +2039,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-scalar"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba68111c567922c6f5d72638361b766cb9871b686c2bee633ea83af1a525b04"
+checksum = "7fa556a8563fb0442344b7f3a123a53dc9bd74ab63f2d2a3c689cf9b8798bc06"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -2051,9 +2051,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-console-types-string"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a9537a4b0769d08c9a08e2ffdbe7976cd33256d8d35b2c5ed74e3c359f7ca8"
+checksum = "afb9c2151d6a99f760bee50df25aa6b32e5bbed9f9d677dc21d262842932cdcb"
 dependencies = [
  "snarkvm-console-network-environment",
  "snarkvm-console-types-boolean",
@@ -2063,9 +2063,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-curves"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65c5e993310c107527fb3e188604bf211fead992acfc93ed44e3cbaf4d0ad373"
+checksum = "c0676eba867fd9c0a77919506e382bc4afe96f2699668f0e1b58386a688886ff"
 dependencies = [
  "rand",
  "rayon",
@@ -2078,9 +2078,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-fields"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1163afb752f484f4d4f9a54af171ce0493c558390a6787259de841f32dda6f5e"
+checksum = "770735a0ba7fb573bf4d7a5e36c3babdaecb3f25ca1dae79b348d76c9df4161c"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -2097,9 +2097,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-authority"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a497d53f3d49660d1b76cd2001e9d6c0acdab133391576cca56b888b541c539b"
+checksum = "4e3263f84bb48f50ef7422eaacab347c931b46242676def1cf36bf8fd5a23e54"
 dependencies = [
  "anyhow",
  "rand",
@@ -2110,9 +2110,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-block"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ed1759fe75c5441b815c16f493447d8bed844d8cd5054c237d20e7fd31b96fd"
+checksum = "60d35da2edc62a32b0d522d3283fddd6556326c9fd6a378456fe1334f13f42fc"
 dependencies = [
  "indexmap 2.0.2",
  "rayon",
@@ -2129,9 +2129,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-coinbase"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91df6b23f0762bc9d3fee18f5d41983b230eddd030dd9551b27b3f07bb81f346"
+checksum = "0db2eb7c2e2723241ac9302aaefe4c852e13d6a00cbba8feb781c48ecb801ae0"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -2150,9 +2150,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-committee"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f608cd8f300b261330da4bad52b77ade130b7d11479d18a2524abe475bafe532"
+checksum = "b8d0178ee395f0c6816fe728cf3b04c28ff1654a00ed5b21bceabfc975e15c53"
 dependencies = [
  "indexmap 2.0.2",
  "serde_json",
@@ -2161,9 +2161,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-narwhal-batch-certificate"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6014a348cebc0c060b2dbf58a406af58fd2d16e68da8a3abc83e5b09bc923df1"
+checksum = "d5435688eb5db7be8f18501fc76dd52e77d51eff25ce934925e3b881e0966431"
 dependencies = [
  "indexmap 2.0.2",
  "serde_json",
@@ -2174,9 +2174,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-narwhal-batch-header"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8622d7af201d5659281948e77b70d93c94ce84f0451a72111888b388817809dc"
+checksum = "648861a8c25712b955f3d5946db958f5c740596962732087c34fbe465b1e78b1"
 dependencies = [
  "indexmap 2.0.2",
  "serde_json",
@@ -2186,9 +2186,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-narwhal-subdag"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9527434e9e8ecc4ee712a39fe08a50011fce52364b4ff87b3c979e135cbd6375"
+checksum = "1a334dadac03e278117a233ed4d036c5239d25a0d87a90c6f73638620210b3b9"
 dependencies = [
  "indexmap 2.0.2",
  "rayon",
@@ -2200,9 +2200,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-narwhal-transmission-id"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d088a1f301d0c3098007fdf6911a9aab67f29cf741c8679f7945f1a7753d83b"
+checksum = "c9128f7a2f60853f9ed88df2ced9c288cb9c55e4a6fdfc70414ac98c136ee793"
 dependencies = [
  "snarkvm-console",
  "snarkvm-ledger-coinbase",
@@ -2210,9 +2210,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-query"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b29b445fdd8284d719fa069935b960e204d5edad9ead544698ab2e348c71d76"
+checksum = "a8e2236c4a459d33d793b0214f2f36e86be1402b50b8764925792292d2655e2a"
 dependencies = [
  "async-trait",
  "reqwest",
@@ -2224,9 +2224,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-ledger-store"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2f3712e245c6d7de7c64a46621360847c289c8ceb57c91c94333d4b63a015b"
+checksum = "0aadc60f9aa02273689bfa8d3dc758a2b1243e78ded3a98ee32b8785f5ab0927"
 dependencies = [
  "anyhow",
  "bincode",
@@ -2246,9 +2246,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-parameters"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bbbdba534b285073f5f4b4073c1fc99b3153d11b47328617d40879a5c145501"
+checksum = "3f6fa068af0b84484d9d45973973c81797b23af7aaf2a9e94d37fa6aabc95899"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -2274,9 +2274,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-synthesizer"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff165e92b0f29a8d6a465833db2e2418ad89e6ad464b65db856de16ecc4ef5f0"
+checksum = "d5c9b356d418f942bc84e758d8feb5422862f3ff4f75d7146cb015c3336cd7ae"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -2300,9 +2300,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-synthesizer-process"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1ba0c718c8520c443a998ae06441deb15020b4f200ad765bb43a3586ad4b476"
+checksum = "9594461d2c1e230329234bdd02f32227f5386885391e6dffb5ec708cf4f9bd9f"
 dependencies = [
  "aleo-std",
  "colored",
@@ -2323,9 +2323,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-synthesizer-program"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8d29cfa2a80bdcf88f02fcef88620ee9b1806260c61fec0e749ce2f46f3f4c5"
+checksum = "bf10d9b11062d76ba99a9313900395378543d6545a3cc1025afb70f9f6890e89"
 dependencies = [
  "indexmap 2.0.2",
  "paste",
@@ -2338,9 +2338,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-synthesizer-snark"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf09606ff2cb8ce7535a9d48403e6a33683b80e16a0469d8ab2e22b39928009f"
+checksum = "7f9cf76c1a25a1ce0a64fb587c8aa5e5949a6166c03a4e4cc079380f07ea6512"
 dependencies = [
  "bincode",
  "once_cell",
@@ -2352,9 +2352,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-utilities"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e4eb9facf3773f9db81f971651f6879d09b5d237154866fed911a0405ab33e6"
+checksum = "7e4808f52a6f0c2a13df5b65706dda2ca64ca6c0fb785cf48dc2d246853d724a"
 dependencies = [
  "aleo-std",
  "anyhow",
@@ -2374,9 +2374,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-utilities-derives"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cdfb2f27da0e1cc4c104d9a88ffc554996a4d3525ac56adbaa55d8ba9e477bd"
+checksum = "b89d440d3c45df5de552f1e4f84549204f67a39104e638f5552fa1572cc7901a"
 dependencies = [
  "proc-macro2",
  "quote 1.0.33",
@@ -2385,9 +2385,9 @@ dependencies = [
 
 [[package]]
 name = "snarkvm-wasm"
-version = "0.16.1"
+version = "0.16.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4fe6f08f4c55464bc643b176727150b56ade1b291fbb204122197b6209609c5"
+checksum = "f68ff316382177726460cca55d825756dab450742c6aa6c2b69b07e56f69b86f"
 dependencies = [
  "getrandom",
  "rand",
diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml
index 8af4f0dd6..d32d86f30 100644
--- a/wasm/Cargo.toml
+++ b/wasm/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "aleo-wasm"
-version = "0.6.2"
+version = "0.6.5"
 authors = [ "The Aleo Team " ]
 description = "WebAssembly based toolkit for developing zero knowledge applications with Aleo"
 homepage = "https://aleo.org"
@@ -22,29 +22,29 @@ crate-type = [ "cdylib", "rlib" ]
 doctest = false
 
 [dependencies.snarkvm-circuit-network]
-version = "0.16.1"
+version = "0.16.6"
 
 [dependencies.snarkvm-console]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "wasm" ]
 
 [dependencies.snarkvm-ledger-block]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "wasm" ]
 
 [dependencies.snarkvm-ledger-query]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "async", "wasm" ]
 
 [dependencies.snarkvm-ledger-store]
-version = "0.16.1"
+version = "0.16.6"
 
 [dependencies.snarkvm-synthesizer]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "async", "wasm" ]
 
 [dependencies.snarkvm-wasm]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "console", "fields", "utilities" ]
 
 [dependencies.anyhow]
@@ -98,7 +98,7 @@ version = "0.1.7"
 version = "1.0.183"
 
 [dev-dependencies.snarkvm-parameters]
-version = "0.16.1"
+version = "0.16.6"
 features = [ "wasm" ]
 
 [dependencies.spmc]
diff --git a/wasm/src/programs/response.rs b/wasm/src/programs/response.rs
index 92008c586..fc1bf0380 100644
--- a/wasm/src/programs/response.rs
+++ b/wasm/src/programs/response.rs
@@ -56,6 +56,7 @@ impl ExecutionResponse {
         let verifying_key = process
             .get_verifying_key(program.id(), function_id)
             .map_err(|_| format!("Could not find verifying key for {:?}/{:?}", program.id(), function_id))?;
+
         Ok(Self {
             execution,
             response,

From 362472cf3b785a0a85dea9a4b9b216c39fd1812f Mon Sep 17 00:00:00 2001
From: Michael Turner 
Date: Thu, 26 Oct 2023 18:32:46 +0200
Subject: [PATCH 3/3] Update nodejs create-aleo-app example to include
 execution verification

---
 create-aleo-app/template-node/index.js | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/create-aleo-app/template-node/index.js b/create-aleo-app/template-node/index.js
index 99fd72610..c09baf049 100644
--- a/create-aleo-app/template-node/index.js
+++ b/create-aleo-app/template-node/index.js
@@ -20,6 +20,7 @@ async function localProgramExecution(program, aleoFunction, inputs) {
 
     // Create a key provider in order to re-use the same key for each execution
     const keyProvider = new AleoKeyProvider();
+    keyProvider.useCache(true);
     programManager.setKeyProvider(keyProvider);
 
     // Pre-synthesize the program keys and then cache them in memory using key provider
@@ -33,30 +34,24 @@ async function localProgramExecution(program, aleoFunction, inputs) {
     // Execute once using the key provider params defined above. This will use the cached proving keys and make
     // execution significantly faster.
     let executionResponse = await programManager.executeOffline(
-        hello_hello_program,
-        "hello",
-        ["5u32", "5u32"],
-        false,
-        undefined,
-        keyProviderParams,
-    );
-    console.log(executionResponse.getOutputs())
-
-    // Execute a second time to ensure the cached keys are still being used - however, this time prove the execution.
-    executionResponse = await programManager.executeOffline(
         hello_hello_program,
         "hello",
         ["5u32", "5u32"],
         true,
+        undefined,
         keyProviderParams,
     );
+    console.log("hello_hello/hello executed - result:", executionResponse.getOutputs());
 
     // Verify the execution using the verifying key that was generated earlier.
-    programManager.verifyExecution(executionResponse);
+    if (programManager.verifyExecution(executionResponse)) {
+        console.log("hello_hello/hello execution verified!");
+    } else {
+        throw("Execution failed verification!");
+    }
 }
 
 const start = Date.now();
 console.log("Starting execute!");
-const result = await localProgramExecution();
-console.log(result);
+await localProgramExecution();
 console.log("Execute finished!", Date.now() - start);