mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-26 16:37:30 +03:00
local execution working for both testnet
and mainnet
This commit is contained in:
parent
9b79386014
commit
9eaddfcea9
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1555,6 +1555,7 @@ dependencies = [
|
|||||||
"leo-parser",
|
"leo-parser",
|
||||||
"leo-retriever",
|
"leo-retriever",
|
||||||
"leo-span",
|
"leo-span",
|
||||||
|
"num-format",
|
||||||
"rand",
|
"rand",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@ -1585,6 +1586,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"leo-errors",
|
"leo-errors",
|
||||||
"leo-retriever",
|
"leo-retriever",
|
||||||
|
"num-format",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
@ -61,6 +61,7 @@ ci_skip = [ "leo-compiler/ci_skip" ]
|
|||||||
noconfig = [ ]
|
noconfig = [ ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
num-format = "0.4.4"
|
||||||
text-tables = "0.3.1"
|
text-tables = "0.3.1"
|
||||||
ureq = "2.9.7"
|
ureq = "2.9.7"
|
||||||
|
|
||||||
|
@ -411,4 +411,11 @@ create_messages!(
|
|||||||
msg: format!("❌ The public balance of {balance} is insufficient to pay the base fee of {fee}"),
|
msg: format!("❌ The public balance of {balance} is insufficient to pay the base fee of {fee}"),
|
||||||
help: None,
|
help: None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@backtraced
|
||||||
|
execution_error {
|
||||||
|
args: (error: impl Display),
|
||||||
|
msg: format!("❌ Execution error: {error}"),
|
||||||
|
help: None,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use super::*;
|
use super::*;
|
||||||
use aleo_std::StorageMode;
|
use aleo_std::StorageMode;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -38,8 +39,12 @@ use snarkvm::{
|
|||||||
Program as SnarkVMProgram,
|
Program as SnarkVMProgram,
|
||||||
ProgramID,
|
ProgramID,
|
||||||
VM,
|
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
|
/// Build, Prove and Run Leo program with inputs
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -82,21 +87,18 @@ impl Command for Execute {
|
|||||||
// Parse the network.
|
// Parse the network.
|
||||||
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
||||||
match network {
|
match network {
|
||||||
NetworkName::MainnetV0 => handle_execute::<MainnetV0>(self, context),
|
NetworkName::MainnetV0 => handle_execute::<AleoV0>(self, context),
|
||||||
NetworkName::TestnetV0 => handle_execute::<TestnetV0>(self, context),
|
NetworkName::TestnetV0 => handle_execute::<AleoTestnetV0>(self, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper function to handle the `execute` command.
|
// A helper function to handle the `execute` command.
|
||||||
fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Execute as Command>::Output> {
|
fn handle_execute<A: Aleo>(command: Execute, context: Context) -> Result<<Execute as Command>::Output> {
|
||||||
// If input values are provided, then run the program with those inputs.
|
// If input values are provided, then run the program with those inputs.
|
||||||
// Otherwise, use the input file.
|
// Otherwise, use the input file.
|
||||||
let mut inputs = command.inputs.clone();
|
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.
|
// Add the inputs to the arguments.
|
||||||
match command.file.clone() {
|
match command.file.clone() {
|
||||||
Some(file) => {
|
Some(file) => {
|
||||||
@ -107,7 +109,7 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
// Parse the values from the file.
|
// Parse the values from the file.
|
||||||
let mut content = raw_content.as_str();
|
let mut content = raw_content.as_str();
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
while let Ok((remaining, value)) = snarkvm::prelude::Value::<N>::parse(content) {
|
while let Ok((remaining, value)) = snarkvm::prelude::Value::<A::Network>::parse(content) {
|
||||||
content = remaining;
|
content = remaining;
|
||||||
values.push(value);
|
values.push(value);
|
||||||
}
|
}
|
||||||
@ -118,17 +120,28 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
// Convert the values to strings.
|
// Convert the values to strings.
|
||||||
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
||||||
// Add the inputs from the file to the arguments.
|
// 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 the `broadcast` flag is set, then broadcast the transaction.
|
||||||
if command.broadcast {
|
if command.broadcast {
|
||||||
// Get the program name.
|
// Get the program name.
|
||||||
let program_name = match (command.program.clone(), command.local) {
|
let program_name = match (command.program.clone(), command.local) {
|
||||||
(Some(name), true) => {
|
(Some(name), true) => {
|
||||||
let local = context.open_manifest::<N>()?.program_id().to_string();
|
let local = context.open_manifest::<A::Network>()?.program_id().to_string();
|
||||||
// Throw error if local name doesn't match the specified name.
|
// Throw error if local name doesn't match the specified name.
|
||||||
if name == local {
|
if name == local {
|
||||||
local
|
local
|
||||||
@ -137,33 +150,22 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Some(name), false) => name.clone(),
|
(Some(name), false) => name.clone(),
|
||||||
(None, true) => context.open_manifest::<N>()?.program_id().to_string(),
|
(None, true) => context.open_manifest::<A::Network>()?.program_id().to_string(),
|
||||||
(None, false) => return Err(PackageError::missing_on_chain_program_name().into()),
|
(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
|
// Specify the query
|
||||||
let query = SnarkVMQuery::<N, BlockMemory<N>>::from(command.compiler_options.endpoint.clone());
|
let query = SnarkVMQuery::<A::Network, BlockMemory<A::Network>>::from(command.compiler_options.endpoint.clone());
|
||||||
|
|
||||||
// Initialize an RNG.
|
|
||||||
let rng = &mut rand::thread_rng();
|
|
||||||
|
|
||||||
// Initialize the storage.
|
// Initialize the storage.
|
||||||
let store = ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?;
|
let store = ConsensusStore::<A::Network, ConsensusMemory<A::Network>>::open(StorageMode::Production)?;
|
||||||
|
|
||||||
// Initialize the VM.
|
// Initialize the VM.
|
||||||
let vm = VM::from(store)?;
|
let vm = VM::from(store)?;
|
||||||
|
|
||||||
// Load the main program, and all of its imports.
|
// Load the main program, and all of its imports.
|
||||||
let program_id = &ProgramID::<N>::from_str(&format!("{}.aleo", program_name))?;
|
let program_id = &ProgramID::<A::Network>::from_str(&format!("{}.aleo", program_name))?;
|
||||||
// TODO: create a local version too
|
// TODO: X
|
||||||
load_program_from_network(&command, context.clone(), &mut vm.process().write(), program_id)?;
|
load_program_from_network(&command, context.clone(), &mut vm.process().write(), program_id)?;
|
||||||
|
|
||||||
let fee_record = if let Some(record) = command.fee_options.record {
|
let fee_record = if let Some(record) = command.fee_options.record {
|
||||||
@ -193,7 +195,7 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
// Check if the public balance is sufficient.
|
// Check if the public balance is sufficient.
|
||||||
if fee_record.is_none() {
|
if fee_record.is_none() {
|
||||||
// Derive the account address.
|
// Derive the account address.
|
||||||
let address = Address::<N>::try_from(ViewKey::try_from(&private_key)?)?;
|
let address = Address::<A::Network>::try_from(ViewKey::try_from(&private_key)?)?;
|
||||||
// Query the public balance of the address on the `account` mapping from `credits.aleo`.
|
// Query the public balance of the address on the `account` mapping from `credits.aleo`.
|
||||||
let mut public_balance = Query {
|
let mut public_balance = Query {
|
||||||
endpoint: command.compiler_options.endpoint.clone(),
|
endpoint: command.compiler_options.endpoint.clone(),
|
||||||
@ -215,14 +217,6 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the cost breakdown.
|
|
||||||
execution_cost_breakdown(
|
|
||||||
&program_name,
|
|
||||||
total_cost as f64 / 1_000_000.0,
|
|
||||||
storage_cost as f64 / 1_000_000.0,
|
|
||||||
finalize_cost as f64 / 1_000_000.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("✅ Created execution transaction for '{}'", program_id.to_string().bold());
|
println!("✅ Created execution transaction for '{}'", program_id.to_string().bold());
|
||||||
|
|
||||||
// Broadcast the execution transaction.
|
// Broadcast the execution transaction.
|
||||||
@ -240,34 +234,81 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the compiler options to the arguments.
|
|
||||||
if command.compiler_options.offline {
|
|
||||||
arguments.push(String::from("--offline"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the endpoint to the arguments.
|
|
||||||
arguments.push(String::from("--endpoint"));
|
|
||||||
arguments.push(command.compiler_options.endpoint.clone());
|
|
||||||
|
|
||||||
// Open the Leo build/ directory.
|
// Open the Leo build/ directory.
|
||||||
let path = context.dir()?;
|
let path = context.dir()?.join("build/");
|
||||||
let build_directory = BuildDirectory::open(&path)?;
|
|
||||||
|
|
||||||
// Change the cwd to the Leo build/ directory to compile aleo files.
|
|
||||||
std::env::set_current_dir(&build_directory)
|
|
||||||
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
|
||||||
|
|
||||||
// Unset the Leo panic hook.
|
// Unset the Leo panic hook.
|
||||||
let _ = std::panic::take_hook();
|
let _ = std::panic::take_hook();
|
||||||
|
|
||||||
// Call the `execute` command.
|
// Conduct the execution locally (code lifted from snarkVM).
|
||||||
|
// Load the package.
|
||||||
|
let package = SnarkVMPackage::open(&path)?;
|
||||||
|
// Convert the inputs.
|
||||||
|
let inputs = inputs.iter().map(|input| Value::from_str(input).unwrap()).collect::<Vec<Value<A::Network>>>();
|
||||||
|
// Execute the request.
|
||||||
|
let (response, execution, metrics) =
|
||||||
|
package.execute::<A, _>(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::<String, usize>::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!();
|
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.
|
// Print the transaction.
|
||||||
tracing::info!("{}", res);
|
println!("{transaction}\n");
|
||||||
|
|
||||||
|
// Prepare the locator.
|
||||||
|
let locator = Locator::<A::Network>::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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,9 @@ default-features = false
|
|||||||
version = "1.9"
|
version = "1.9"
|
||||||
features = [ "serde" ]
|
features = [ "serde" ]
|
||||||
|
|
||||||
|
[dependencies.num-format]
|
||||||
|
version = "0.4.4"
|
||||||
|
|
||||||
[dependencies.rand]
|
[dependencies.rand]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user