require confirmation for execution/deployment unless -y or --yes present

This commit is contained in:
evan-schott 2024-06-12 13:45:57 -07:00
parent b7eac78644
commit b79c0a1ab6
6 changed files with 72 additions and 11 deletions

21
Cargo.lock generated
View File

@ -742,6 +742,19 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "dialoguer"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
dependencies = [
"console",
"shell-words",
"tempfile",
"thiserror",
"zeroize",
]
[[package]]
name = "difflib"
version = "0.4.0"
@ -1544,6 +1557,7 @@ dependencies = [
"colored",
"console",
"crossterm",
"dialoguer",
"dirs 5.0.1",
"dotenvy",
"indexmap 1.9.3",
@ -1582,6 +1596,7 @@ name = "leo-package"
version = "1.12.0"
dependencies = [
"aleo-std",
"dialoguer",
"indexmap 1.9.3",
"lazy_static",
"leo-errors",
@ -2740,6 +2755,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shell-words"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "signal-hook"
version = "0.3.17"

View File

@ -61,6 +61,7 @@ ci_skip = [ "leo-compiler/ci_skip" ]
noconfig = [ ]
[dependencies]
dialoguer = "0.11.0"
num-format = "0.4.4"
text-tables = "0.3.1"
ureq = "2.9.7"

View File

@ -16,6 +16,7 @@
use super::*;
use aleo_std::StorageMode;
use dialoguer::{theme::ColorfulTheme, Confirm};
use leo_retriever::NetworkName;
use snarkvm::{
circuit::{Aleo, AleoTestnetV0, AleoV0},
@ -94,6 +95,7 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
&dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
)?,
};
let address = Address::try_from(&private_key)?;
// Specify the query
let query = SnarkVMQuery::from(&command.options.endpoint);
@ -185,7 +187,22 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
// Determine if the transaction should be broadcast, stored, or displayed to the user.
if !command.fee_options.dry_run {
println!("✅ Created deployment transaction for '{}'", name.bold());
if !command.fee_options.yes {
let prompt = format!(
"Do you want to submit deployment of program `{name}.aleo` to network {} via endpoint {} using address {}?",
command.options.network, command.options.endpoint, address
);
let confirmation = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.default(false)
.interact()
.unwrap();
if !confirmation {
println!("✅ Successfully aborted the execution transaction for '{}'\n", name.bold());
return Ok(());
}
}
println!("✅ Created deployment transaction for '{}'\n", name.bold());
handle_broadcast(
&format!("{}/{}/transaction/broadcast", command.options.endpoint, command.options.network),
transaction,
@ -196,7 +213,7 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
std::thread::sleep(std::time::Duration::from_secs(command.wait));
}
} else {
println!("✅ Successful dry run deployment for '{}'", name.bold());
println!("✅ Successful dry run deployment for '{}'\n", name.bold());
}
}
@ -212,7 +229,7 @@ fn deploy_cost_breakdown(
namespace_cost: f64,
priority_fee: f64,
) {
println!("Base deployment cost for '{}' is {} credits.", name.bold(), total_cost);
println!("\nBase deployment cost for '{}' is {} credits.\n", name.bold(), total_cost);
// Display the cost breakdown in a table.
let data = [
[name, "Cost (credits)"],

View File

@ -24,6 +24,7 @@ use snarkvm::{
use std::collections::HashMap;
use crate::cli::query::QueryCommands;
use dialoguer::{theme::ColorfulTheme, Confirm};
use leo_retriever::NetworkName;
use snarkvm::{
circuit::{Aleo, AleoTestnetV0, AleoV0},
@ -133,6 +134,7 @@ fn handle_execute<A: Aleo>(command: Execute, context: Context) -> Result<<Execut
&dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
)?,
};
let address = Address::try_from(&private_key)?;
// If the `broadcast` flag is set, then broadcast the transaction.
if command.broadcast {
@ -176,7 +178,7 @@ fn handle_execute<A: Aleo>(command: Execute, context: Context) -> Result<<Execut
// Create a new transaction.
let transaction = vm.execute(
&private_key,
(program_id, command.name),
(program_id, command.name.clone()),
inputs.iter(),
fee_record.clone(),
command.fee_options.priority_fee,
@ -215,7 +217,22 @@ fn handle_execute<A: Aleo>(command: Execute, context: Context) -> Result<<Execut
// Broadcast the execution transaction.
if !command.fee_options.dry_run {
println!("✅ Created execution transaction for '{}'", program_id.to_string().bold());
if !command.fee_options.yes {
let prompt = format!(
"Do you want to submit execution of function `{}` on program `{program_name}.aleo` to network {} via endpoint {} using address {}?",
&command.name, command.compiler_options.network, command.compiler_options.endpoint, address
);
let confirmation = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.default(false)
.interact()
.unwrap();
if !confirmation {
println!("✅ Successfully aborted the execution transaction for '{}'\n", program_name.bold());
return Ok(());
}
}
println!("✅ Created execution transaction for '{}'\n", program_id.to_string().bold());
handle_broadcast(
&format!(
"{}/{}/transaction/broadcast",
@ -225,7 +242,7 @@ fn handle_execute<A: Aleo>(command: Execute, context: Context) -> Result<<Execut
&program_name,
)?;
} else {
println!("✅ Successful dry run execution for '{}'", program_id.to_string().bold());
println!("✅ Successful dry run execution for '{}'\n", program_id.to_string().bold());
}
return Ok(());
@ -362,7 +379,7 @@ fn load_program_from_network<N: Network>(
// 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, priority_fee: f64) {
println!("Base execution cost for '{}' is {} credits.", name.bold(), total_cost);
println!("\nBase execution cost for '{}' is {} credits.\n", name.bold(), total_cost);
// Display the cost breakdown in a table.
let data = [
[name, "Cost (credits)"],

View File

@ -210,6 +210,8 @@ impl Default for BuildOptions {
/// Used by Execute and Deploy commands.
#[derive(Parser, Clone, Debug, Default)]
pub struct FeeOptions {
#[clap(short, long, help = "Don't ask for confirmation.", default_value = "false")]
pub(crate) yes: bool,
#[clap(short, long, help = "Performs a dry-run of transaction generation")]
pub(crate) dry_run: bool,
#[clap(long, help = "Priority fee in microcredits. Defaults to 0.", default_value = "0")]
@ -273,7 +275,7 @@ fn check_balance<N: Network>(
/// Determine if the transaction should be broadcast or displayed to user.
fn handle_broadcast<N: Network>(endpoint: &String, transaction: Transaction<N>, operation: &String) -> Result<()> {
println!("Broadcasting transaction to {}...", endpoint.clone());
println!("Broadcasting transaction to {}...\n", endpoint.clone());
// Get the transaction id.
let transaction_id = transaction.id();
@ -287,20 +289,20 @@ fn handle_broadcast<N: Network>(endpoint: &String, transaction: Transaction<N>,
match transaction {
Transaction::Deploy(..) => {
println!(
"⌛ Deployment {transaction_id} ('{}') has been broadcast to {}.",
"⌛ Deployment {transaction_id} ('{}') has been broadcast to {}.\n",
operation.bold(),
endpoint
)
}
Transaction::Execute(..) => {
println!(
"⌛ Execution {transaction_id} ('{}') has been broadcast to {}.",
"⌛ Execution {transaction_id} ('{}') has been broadcast to {}.\n",
operation.bold(),
endpoint
)
}
Transaction::Fee(..) => {
println!("❌ Failed to broadcast fee '{}' to the {}.", operation.bold(), endpoint)
println!("❌ Failed to broadcast fee '{}' to the {}.\n", operation.bold(), endpoint)
}
}
Ok(())

View File

@ -37,6 +37,9 @@ default-features = false
version = "1.9"
features = [ "serde" ]
[dependencies.dialoguer]
version = "0.11.0"
[dependencies.num-format]
version = "0.4.4"