diff --git a/Cargo.lock b/Cargo.lock
index 9bca9db2f1..a99b61ba81 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1555,6 +1555,7 @@ dependencies = [
"leo-parser",
"leo-retriever",
"leo-span",
+ "num-format",
"rand",
"rand_chacha",
"rand_core",
@@ -1585,6 +1586,7 @@ dependencies = [
"lazy_static",
"leo-errors",
"leo-retriever",
+ "num-format",
"rand",
"serde",
"serial_test",
diff --git a/Cargo.toml b/Cargo.toml
index 5e015daa67..08e094dd42 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -61,6 +61,7 @@ ci_skip = [ "leo-compiler/ci_skip" ]
noconfig = [ ]
[dependencies]
+num-format = "0.4.4"
text-tables = "0.3.1"
ureq = "2.9.7"
diff --git a/errors/src/errors/package/package_errors.rs b/errors/src/errors/package/package_errors.rs
index e8157b89d3..7346d39705 100644
--- a/errors/src/errors/package/package_errors.rs
+++ b/errors/src/errors/package/package_errors.rs
@@ -411,4 +411,11 @@ create_messages!(
msg: format!("❌ The public balance of {balance} is insufficient to pay the base fee of {fee}"),
help: None,
}
+
+ @backtraced
+ execution_error {
+ args: (error: impl Display),
+ msg: format!("❌ Execution error: {error}"),
+ help: None,
+ }
);
diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs
index e40df6b49c..beed7cc6dd 100644
--- a/leo/cli/commands/execute.rs
+++ b/leo/cli/commands/execute.rs
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see .
+use std::collections::HashMap;
use super::*;
use aleo_std::StorageMode;
use clap::Parser;
@@ -38,8 +39,12 @@ use snarkvm::{
Program as SnarkVMProgram,
ProgramID,
VM,
- },
+ },
+ package::Package as SnarkVMPackage,
};
+use snarkvm::circuit::{Aleo, AleoTestnetV0, AleoV0};
+use snarkvm::cli::LOCALE;
+use snarkvm::prelude::{Identifier, Locator, Value};
/// Build, Prove and Run Leo program with inputs
#[derive(Parser, Debug)]
@@ -82,21 +87,18 @@ impl Command for Execute {
// Parse the network.
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
match network {
- NetworkName::MainnetV0 => handle_execute::(self, context),
- NetworkName::TestnetV0 => handle_execute::(self, context),
+ NetworkName::MainnetV0 => handle_execute::(self, context),
+ NetworkName::TestnetV0 => handle_execute::(self, context),
}
}
}
// A helper function to handle the `execute` command.
-fn handle_execute(command: Execute, context: Context) -> Result<::Output> {
+fn handle_execute(command: Execute, context: Context) -> Result<::Output> {
// If input values are provided, then run the program with those inputs.
// Otherwise, use the input file.
let mut inputs = command.inputs.clone();
- // Compose the `execute` command.
- let mut arguments = vec![SNARKVM_COMMAND.to_string(), command.name.clone()];
-
// Add the inputs to the arguments.
match command.file.clone() {
Some(file) => {
@@ -107,7 +109,7 @@ fn handle_execute(command: Execute, context: Context) -> Result<::parse(content) {
+ while let Ok((remaining, value)) = snarkvm::prelude::Value::::parse(content) {
content = remaining;
values.push(value);
}
@@ -118,17 +120,28 @@ fn handle_execute(command: Execute, context: Context) -> Result<>();
// Add the inputs from the file to the arguments.
- arguments.append(&mut inputs_from_file);
+ inputs.append(&mut inputs_from_file);
}
- None => arguments.append(&mut inputs),
+ None => {},
}
+ // Initialize an RNG.
+ let rng = &mut rand::thread_rng();
+
+ // Get the private key.
+ let private_key = match command.fee_options.private_key.clone() {
+ Some(key) => PrivateKey::from_str(&key)?,
+ None => PrivateKey::from_str(
+ &dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
+ )?,
+ };
+
// If the `broadcast` flag is set, then broadcast the transaction.
if command.broadcast {
// Get the program name.
let program_name = match (command.program.clone(), command.local) {
(Some(name), true) => {
- let local = context.open_manifest::()?.program_id().to_string();
+ let local = context.open_manifest::()?.program_id().to_string();
// Throw error if local name doesn't match the specified name.
if name == local {
local
@@ -137,33 +150,22 @@ fn handle_execute(command: Execute, context: Context) -> Result< name.clone(),
- (None, true) => context.open_manifest::()?.program_id().to_string(),
+ (None, true) => context.open_manifest::()?.program_id().to_string(),
(None, false) => return Err(PackageError::missing_on_chain_program_name().into()),
};
- // Get the private key.
- let private_key = match command.fee_options.private_key.clone() {
- Some(key) => PrivateKey::from_str(&key)?,
- None => PrivateKey::from_str(
- &dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
- )?,
- };
-
// Specify the query
- let query = SnarkVMQuery::>::from(command.compiler_options.endpoint.clone());
-
- // Initialize an RNG.
- let rng = &mut rand::thread_rng();
+ let query = SnarkVMQuery::>::from(command.compiler_options.endpoint.clone());
// Initialize the storage.
- let store = ConsensusStore::>::open(StorageMode::Production)?;
+ let store = ConsensusStore::>::open(StorageMode::Production)?;
// Initialize the VM.
let vm = VM::from(store)?;
// Load the main program, and all of its imports.
- let program_id = &ProgramID::::from_str(&format!("{}.aleo", program_name))?;
- // TODO: create a local version too
+ let program_id = &ProgramID::::from_str(&format!("{}.aleo", program_name))?;
+ // TODO: X
load_program_from_network(&command, context.clone(), &mut vm.process().write(), program_id)?;
let fee_record = if let Some(record) = command.fee_options.record {
@@ -193,7 +195,7 @@ fn handle_execute(command: Execute, context: Context) -> Result<::try_from(ViewKey::try_from(&private_key)?)?;
+ let address = Address::::try_from(ViewKey::try_from(&private_key)?)?;
// Query the public balance of the address on the `account` mapping from `credits.aleo`.
let mut public_balance = Query {
endpoint: command.compiler_options.endpoint.clone(),
@@ -215,14 +217,6 @@ fn handle_execute(command: Execute, context: Context) -> Result<(command: Execute, context: Context) -> Result<>>();
+ // Execute the request.
+ let (response, execution, metrics) =
+ package.execute::(command.compiler_options.endpoint.clone(), &private_key, Identifier::try_from(command.name.clone())?, &inputs, rng).map_err(|err| PackageError::execution_error(err))?;
+
+ let fee = None;
+
+ // Construct the transaction.
+ let transaction = Transaction::from_execution(execution, fee)?;
+
+ // Log the metrics.
+ use num_format::ToFormattedString;
+
+ // Count the number of times a function is called.
+ let mut program_frequency = HashMap::::new();
+ for metric in metrics.iter() {
+ // Prepare the function name string.
+ let function_name_string = format!("'{}/{}'", metric.program_id, metric.function_name).bold();
+
+ // Prepare the function constraints string
+ let function_constraints_string = format!(
+ "{function_name_string} - {} constraints",
+ metric.num_function_constraints.to_formatted_string(LOCALE)
+ );
+
+ // Increment the counter for the function call.
+ match program_frequency.get_mut(&function_constraints_string) {
+ Some(counter) => *counter += 1,
+ None => {
+ let _ = program_frequency.insert(function_constraints_string, 1);
+ }
+ }
+ }
+
+ println!("⛓ Constraints\n");
+ for (function_constraints, counter) in program_frequency {
+ // Log the constraints
+ let counter_string = match counter {
+ 1 => "(called 1 time)".to_string().dimmed(),
+ counter => format!("(called {counter} times)").dimmed(),
+ };
+
+ println!(" • {function_constraints} {counter_string}",)
+ }
+
+ // Log the outputs.
+ match response.outputs().len() {
+ 0 => (),
+ 1 => println!("\n➡️ Output\n"),
+ _ => println!("\n➡️ Outputs\n"),
+ };
+ for output in response.outputs() {
+ println!("{}", format!(" • {output}"));
+ }
println!();
- let command = SnarkVMExecute::try_parse_from(&arguments).map_err(CliError::failed_to_parse_execute)?;
- let res = command.parse().map_err(CliError::failed_to_execute_execute)?;
- // Log the output of the `execute` command.
- tracing::info!("{}", res);
+ // Print the transaction.
+ println!("{transaction}\n");
+ // Prepare the locator.
+ let locator = Locator::::from_str(&format!("{}/{}", package.program_id(), command.name))?;
+ // Prepare the path string.
+ let path_string = format!("(in \"{}\")", path.display());
+
+ println!("✅ Executed '{}' {}", locator.to_string().bold(), path_string.dimmed());
Ok(())
}
diff --git a/leo/package/Cargo.toml b/leo/package/Cargo.toml
index 7369098ec7..ed2c7a6299 100644
--- a/leo/package/Cargo.toml
+++ b/leo/package/Cargo.toml
@@ -37,6 +37,9 @@ default-features = false
version = "1.9"
features = [ "serde" ]
+[dependencies.num-format]
+version = "0.4.4"
+
[dependencies.rand]
version = "0.8"