mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-08-16 09:10:32 +03:00
execute working
This commit is contained in:
parent
f8cef0f8e8
commit
ac5a262e2e
@ -404,4 +404,11 @@ create_messages!(
|
||||
msg: format!("Failed to read manifest file from the provided file path {path} - {error}"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
insufficient_balance {
|
||||
args: (balance: impl Display, fee: impl Display),
|
||||
msg: format!("❌ The public balance of {balance} is insufficient to pay the base fee of {fee}"),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
@ -126,7 +126,6 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
|
||||
let deployment = package.deploy::<A>(None)?;
|
||||
let deployment_id = deployment.to_deployment_id()?;
|
||||
|
||||
|
||||
let store = ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?;
|
||||
|
||||
// Initialize the VM.
|
||||
@ -137,7 +136,7 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
|
||||
|
||||
if command.fee_options.estimate_fee {
|
||||
// Use `credit` denomination instead of `microcredit`.
|
||||
display_cost_breakdown(name, total_cost as f64 / 1_000_000.0, storage_cost as f64 / 1_000_000.0, synthesis_cost as f64 / 1_000_000.0, namespace_cost as f64 / 1_000_000.0);
|
||||
deploy_cost_breakdown(name, total_cost as f64 / 1_000_000.0, storage_cost as f64 / 1_000_000.0, synthesis_cost as f64 / 1_000_000.0, namespace_cost as f64 / 1_000_000.0);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -193,7 +192,7 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
|
||||
}
|
||||
|
||||
// A helper function to display a cost breakdown of the deployment.
|
||||
fn display_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, synthesis_cost: f64, namespace_cost: f64) {
|
||||
fn deploy_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, synthesis_cost: f64, namespace_cost: f64) {
|
||||
println!("✅ Estimated deployment cost for '{}' is {} credits.", name.bold(), total_cost);
|
||||
// Display the cost breakdown in a table.
|
||||
let data = [[name, "Cost (credits)", "Cost reduction tips"], ["Storage", &format!("{:.6}", storage_cost), "Use less instructions"], ["Synthesis", &format!("{:.6}", synthesis_cost), "Remove expensive operations (Ex: SHA3), or unnecessary imports"], ["Namespace", &format!("{:.6}", namespace_cost), "Lengthen the program name (each additional character makes it 10x cheaper)"], ["Total", &format!("{:.6}", total_cost), ""]];
|
||||
|
@ -14,13 +14,22 @@
|
||||
// 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/>.
|
||||
|
||||
use aleo_std::StorageMode;
|
||||
use super::*;
|
||||
use clap::Parser;
|
||||
use leo_retriever::NetworkName;
|
||||
use snarkvm::{
|
||||
cli::{helpers::dotenv_private_key, Execute as SnarkVMExecute},
|
||||
prelude::{MainnetV0, Network, Parser as SnarkVMParser, TestnetV0},
|
||||
};
|
||||
};
|
||||
use snarkvm::file::AleoFile;
|
||||
use snarkvm::ledger::Execution;
|
||||
use snarkvm::ledger::Transaction::Execute as ExecuteTransaction;
|
||||
use snarkvm::prelude::store::ConsensusStore;
|
||||
use snarkvm::prelude::store::helpers::memory::{BlockMemory, ConsensusMemory};
|
||||
use snarkvm::prelude::{Address, execution_cost, Process, Program as SnarkVMProgram, ProgramID, VM};
|
||||
use snarkvm::prelude::query::Query as SnarkVMQuery;
|
||||
use crate::cli::query::QueryCommands;
|
||||
use crate::cli::query::QueryCommands::Program;
|
||||
|
||||
/// Build, Prove and Run Leo program with inputs
|
||||
#[derive(Parser, Debug)]
|
||||
@ -59,7 +68,7 @@ impl Command for Execute {
|
||||
(Build { options: self.compiler_options.clone() }).execute(context)
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _input: Self::Input) -> Result<Self::Output> {
|
||||
fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output> {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
||||
match network {
|
||||
@ -71,80 +80,16 @@ impl Command for Execute {
|
||||
|
||||
// A helper function to handle the `execute` command.
|
||||
fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Execute as Command>::Output> {
|
||||
// If the `broadcast` flag is set, then broadcast the transaction.
|
||||
if command.broadcast {
|
||||
// // Get the program name.
|
||||
// let program_name = match (command.program, command.local) {
|
||||
// (Some(name), true) => {
|
||||
// let local = context.open_manifest::<N>()?.program_id().to_string();
|
||||
// // Throw error if local name doesn't match the specified name.
|
||||
// if name == local {
|
||||
// local
|
||||
// } else {
|
||||
// return Err(PackageError::conflicting_on_chain_program_name(local, name).into());
|
||||
// }
|
||||
// }
|
||||
// (Some(name), false) => name,
|
||||
// (None, true) => context.open_manifest::<N>()?.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 {
|
||||
// Some(private_key) => private_key,
|
||||
// None => dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
|
||||
// };
|
||||
//
|
||||
// // Set deploy arguments.
|
||||
// let mut fee_args = vec![
|
||||
// "snarkos".to_string(),
|
||||
// "--private-key".to_string(),
|
||||
// private_key.clone(),
|
||||
// "--query".to_string(),
|
||||
// command.compiler_options.endpoint.clone(),
|
||||
// "--priority-fee".to_string(),
|
||||
// command.fee_options.priority_fee.to_string(),
|
||||
// "--broadcast".to_string(),
|
||||
// format!("{}/{}/transaction/broadcast", command.compiler_options.endpoint, command.compiler_options.network)
|
||||
// .to_string(),
|
||||
// ];
|
||||
//
|
||||
// // Use record as payment option if it is provided.
|
||||
// if let Some(record) = command.fee_options.record.clone() {
|
||||
// fee_args.push("--record".to_string());
|
||||
// fee_args.push(record);
|
||||
// };
|
||||
//
|
||||
// // Execute program.
|
||||
// Developer::Execute(
|
||||
// SnarkOSExecute::try_parse_from(
|
||||
// [
|
||||
// // The arguments for determining fee.
|
||||
// fee_args,
|
||||
// // The program ID and function name.
|
||||
// vec![program_name, command.name],
|
||||
// // The function inputs.
|
||||
// command.inputs,
|
||||
// ]
|
||||
// .concat(),
|
||||
// )
|
||||
// .unwrap(),
|
||||
// )
|
||||
// .parse()
|
||||
// .map_err(CliError::failed_to_execute_deploy)?;
|
||||
//
|
||||
// return Ok(());
|
||||
}
|
||||
|
||||
// If input values are provided, then run the program with those inputs.
|
||||
// Otherwise, use the input file.
|
||||
let mut inputs = command.inputs;
|
||||
let mut inputs = command.inputs.clone();
|
||||
|
||||
// Compose the `execute` command.
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), command.name];
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), command.name.clone()];
|
||||
|
||||
// Add the inputs to the arguments.
|
||||
match command.file {
|
||||
match command.file.clone() {
|
||||
Some(file) => {
|
||||
// Get the contents from the file.
|
||||
let path = context.dir()?.join(file);
|
||||
@ -169,6 +114,99 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
||||
None => arguments.append(&mut inputs),
|
||||
}
|
||||
|
||||
// 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.clone()) {
|
||||
(Some(name), true) => {
|
||||
let local = context.open_manifest::<N>()?.program_id().to_string();
|
||||
// Throw error if local name doesn't match the specified name.
|
||||
if name == local {
|
||||
local
|
||||
} else {
|
||||
return Err(PackageError::conflicting_on_chain_program_name(local, name).into());
|
||||
}
|
||||
}
|
||||
(Some(name), false) => name.clone(),
|
||||
(None, true) => context.open_manifest::<N>()?.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::<N, BlockMemory<N>>::from(command.compiler_options.endpoint.clone());
|
||||
|
||||
// Initialize an RNG.
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
// Initialize the storage.
|
||||
let store = ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?;
|
||||
|
||||
// Initialize the VM.
|
||||
let vm = VM::from(store)?;
|
||||
|
||||
// Load the main program, and all of its imports.
|
||||
let program_id = &ProgramID::<N>::from_str(&format!("{}.aleo", program_name))?;
|
||||
// TODO: create a local version too
|
||||
load_program_from_network(&command, context.clone(), &mut vm.process().write(), program_id)?;
|
||||
|
||||
let fee_record = if let Some(record) = command.fee_options.record {
|
||||
Some(parse_record(&private_key, &record)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create a new transaction.
|
||||
let transaction = vm.execute(&private_key, (program_id, command.name), inputs.iter(), fee_record.clone(), command.fee_options.priority_fee, Some(query), rng)?;
|
||||
|
||||
// Check the transaction cost.
|
||||
let (total_cost, (storage_cost, finalize_cost)) = if let ExecuteTransaction(_, execution, _) = &transaction {
|
||||
execution_cost(&vm.process().read(), &execution)?
|
||||
} else {
|
||||
panic!("All transactions should be of type Execute.")
|
||||
};
|
||||
|
||||
// Check if the public balance is sufficient.
|
||||
if fee_record.is_none() {
|
||||
// Derive the account address.
|
||||
let address = Address::<N>::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(), network: command.compiler_options.network.clone(), command: QueryCommands::Program {
|
||||
command: crate::cli::commands::query::Program {
|
||||
name: "credits".to_string(),
|
||||
mappings: false,
|
||||
mapping_value: Some(vec!["account".to_string(), address.to_string()]),
|
||||
},
|
||||
} }.execute(context.clone())?;
|
||||
// Check balance.
|
||||
// Remove the last 3 characters since they represent the `u64` suffix.
|
||||
public_balance.truncate(public_balance.len() - 3);
|
||||
if public_balance.parse::<u64>().unwrap() < total_cost {
|
||||
return Err(PackageError::insufficient_balance(public_balance, total_cost).into());
|
||||
}
|
||||
}
|
||||
|
||||
// Print the cost breakdown.
|
||||
if command.fee_options.estimate_fee {
|
||||
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);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("✅ Created execution transaction for '{}'", program_id.to_string().bold());
|
||||
|
||||
handle_broadcast(&format!("{}/{}/transaction/broadcast", command.compiler_options.endpoint, command.compiler_options.network), transaction, &program_name)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Add the compiler options to the arguments.
|
||||
if command.compiler_options.offline {
|
||||
arguments.push(String::from("--offline"));
|
||||
@ -199,3 +237,47 @@ fn handle_execute<N: Network>(command: Execute, context: Context) -> Result<<Exe
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A helper function to recursively load the program and all of its imports into the process. Lifted from snarkOS.
|
||||
fn load_program_from_network<N: Network>(command: &Execute, context: Context, process: &mut Process<N>, program_id: &ProgramID<N>) -> Result<()> {
|
||||
// Fetch the program.
|
||||
let program_src = Query{ endpoint: command.compiler_options.endpoint.clone(), network: command.compiler_options.network.clone(), command: QueryCommands::Program {
|
||||
command: crate::cli::commands::query::Program {
|
||||
name: program_id.to_string(),
|
||||
mappings: false,
|
||||
mapping_value: None,
|
||||
},
|
||||
} }.execute(context.clone())?;
|
||||
let program = SnarkVMProgram::<N>::from_str(&program_src).unwrap();
|
||||
|
||||
// Return early if the program is already loaded.
|
||||
if process.contains_program(program.id()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Iterate through the program imports.
|
||||
for import_program_id in program.imports().keys() {
|
||||
// Add the imports to the process if does not exist yet.
|
||||
if !process.contains_program(import_program_id) {
|
||||
// Recursively load the program and its imports.
|
||||
load_program_from_network(command, context.clone(), process, import_program_id)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the program to the process if it does not already exist.
|
||||
if !process.contains_program(program.id()) {
|
||||
process.add_program(&program)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// A helper function to display a cost breakdown of the execution.
|
||||
fn execution_cost_breakdown(name: &String, total_cost: f64, storage_cost: f64, finalize_cost: f64) {
|
||||
println!("✅ Estimated execution cost for '{}' is {} credits.", name.bold(), total_cost);
|
||||
// Display the cost breakdown in a table.
|
||||
let data = [[name, "Cost (credits)", "Cost reduction tips"], ["Storage", &format!("{:.6}", storage_cost), "Use fewer nested transition functions and smaller input and output datatypes"], ["On-chain", &format!("{:.6}", finalize_cost), "Remove operations that are expensive computationally (Ex: hash functions) or storage-wise (Ex: Mapping insertions)"], ["Total", &format!("{:.6}", total_cost), ""]];
|
||||
let mut out = Vec::new();
|
||||
text_tables::render(&mut out, data).unwrap();
|
||||
println!("{}", ::std::str::from_utf8(&out).unwrap());
|
||||
}
|
@ -240,15 +240,6 @@ fn handle_broadcast<N: Network>(endpoint: &String, transaction: Transaction<N>,
|
||||
// Get the transaction id.
|
||||
let transaction_id = transaction.id();
|
||||
|
||||
// TODO: remove
|
||||
println!("Transaction {:?}", transaction);
|
||||
let tx_bytes = transaction.to_bytes_le()?;
|
||||
println!("Transaction bytes: {:?}", tx_bytes);
|
||||
let tx_json = serde_json::to_string(&transaction).unwrap();
|
||||
println!("Transaction JSON: {:?}", tx_json);
|
||||
let deserialize_tx_json = serde_json::from_str::<Transaction<N>>(&tx_json).unwrap();
|
||||
println!("Deserialized transaction: {:?}", deserialize_tx_json);
|
||||
|
||||
// Send the deployment request to the local development node.
|
||||
return match ureq::post(endpoint).send_json(&transaction) {
|
||||
Ok(id) => {
|
||||
|
@ -19,8 +19,8 @@ use super::*;
|
||||
mod block;
|
||||
use block::Block;
|
||||
|
||||
mod program;
|
||||
use program::Program;
|
||||
pub mod program;
|
||||
pub use program::Program;
|
||||
|
||||
mod state_root;
|
||||
use state_root::StateRoot;
|
||||
@ -53,15 +53,15 @@ pub struct Query {
|
||||
default_value = "http://api.explorer.aleo.org/v1"
|
||||
)]
|
||||
pub endpoint: String,
|
||||
#[clap(short, long, global = true, help = "Network to use. Defaults to testnet3.", default_value = "testnet3")]
|
||||
#[clap(short, long, global = true, help = "Network to use. Defaults to mainnet.", default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
#[clap(subcommand)]
|
||||
command: QueryCommands,
|
||||
pub command: QueryCommands,
|
||||
}
|
||||
|
||||
impl Command for Query {
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
type Output = String;
|
||||
|
||||
fn log_span(&self) -> Span {
|
||||
tracing::span!(tracing::Level::INFO, "Leo")
|
||||
@ -105,8 +105,9 @@ impl Command for Query {
|
||||
if response.status() == 200 {
|
||||
tracing::info!("✅ Successfully retrieved data from '{url}'.\n");
|
||||
// Unescape the newlines.
|
||||
println!("{}\n", response.into_string().unwrap().replace("\\n", "\n"));
|
||||
Ok(())
|
||||
let result = response.into_string().unwrap().replace("\\n", "\n").replace('\"', "");
|
||||
println!("{}\n", result);
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(UtilError::network_error(url, response.status(), Default::default()).into())
|
||||
}
|
||||
@ -114,7 +115,7 @@ impl Command for Query {
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
enum QueryCommands {
|
||||
pub enum QueryCommands {
|
||||
#[clap(about = "Query block information")]
|
||||
Block {
|
||||
#[clap(flatten)]
|
||||
|
Loading…
Reference in New Issue
Block a user