From 16d71a8b67dc85015a9179f35d7a313bb3b86715 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Sun, 8 Dec 2024 21:03:10 -0800 Subject: [PATCH 1/4] Select fee based on consensus height --- leo/cli/commands/deploy.rs | 2 +- leo/cli/commands/execute.rs | 27 ++++++++++++++++++++++++--- leo/cli/commands/mod.rs | 30 ++++++++++++++++++++++++++++-- leo/cli/commands/query/mod.rs | 24 ++++++++++++------------ 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/leo/cli/commands/deploy.rs b/leo/cli/commands/deploy.rs index 860a092e22..74ca27dd09 100644 --- a/leo/cli/commands/deploy.rs +++ b/leo/cli/commands/deploy.rs @@ -193,7 +193,7 @@ fn handle_deploy, N: Network>( } None => { // Make sure the user has enough public balance to pay for the deployment. - check_balance(&private_key, endpoint, &network.to_string(), context.clone(), total_cost)?; + check_balance(&private_key, endpoint, &network.to_string(), &context, total_cost)?; let fee_authorization = vm.authorize_fee_public( &private_key, total_cost, diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs index bcfb6a76f6..e09cde6e64 100644 --- a/leo/cli/commands/execute.rs +++ b/leo/cli/commands/execute.rs @@ -42,6 +42,7 @@ use snarkvm::{ VM, Value, execution_cost_v1, + execution_cost_v2, query::Query as SnarkVMQuery, store::{ ConsensusStore, @@ -213,8 +214,28 @@ fn handle_execute( // Check the transaction cost. let (mut total_cost, (storage_cost, finalize_cost)) = if let ExecuteTransaction(_, execution, _) = &transaction { - // TODO: Update to V2 after block migration. - execution_cost_v1(&vm.process().read(), execution)? + // Attempt to get the height of the latest block to determine which version of the execution cost to use. + if let Ok(height) = get_latest_block_height(endpoint, &network.to_string(), &context) { + if height < A::Network::CONSENSUS_V2_HEIGHT { + execution_cost_v1(&vm.process().read(), execution)? + } else { + execution_cost_v2(&vm.process().read(), execution)? + } + } + // Otherwise, default to the one provided in `fee_options`. + else { + println!( + "Failed to get the latest block height. Defaulting to V{}.", + command.fee_options.consensus_version + ); + match command.fee_options.consensus_version { + 1 => execution_cost_v1(&vm.process().read(), execution)?, + 2 => execution_cost_v2(&vm.process().read(), execution)?, + version => { + panic!("Invalid consensus version: {version}. Please specify a valid version.") + } + } + } } else { panic!("All transactions should be of type Execute.") }; @@ -231,7 +252,7 @@ fn handle_execute( // Check if the public balance is sufficient. if fee_record.is_none() { - check_balance::(&private_key, endpoint, &network.to_string(), context, total_cost)?; + check_balance::(&private_key, endpoint, &network.to_string(), &context, total_cost)?; } // Broadcast the execution transaction. diff --git a/leo/cli/commands/mod.rs b/leo/cli/commands/mod.rs index d22cd8683c..3cabd207d3 100644 --- a/leo/cli/commands/mod.rs +++ b/leo/cli/commands/mod.rs @@ -220,6 +220,8 @@ pub struct FeeOptions { long )] record: Option, + #[clap(long, help = "Consensus version to use for the transaction.", default_value = "2")] + pub(crate) consensus_version: u8, } /// Parses the record string. If the string is a ciphertext, then attempt to decrypt it. Lifted from snarkOS. @@ -241,7 +243,7 @@ fn check_balance( private_key: &PrivateKey, endpoint: &str, network: &str, - context: Context, + context: &Context, total_cost: u64, ) -> Result<()> { // Derive the account address. @@ -251,7 +253,7 @@ fn check_balance( endpoint: Some(endpoint.to_string()), network: Some(network.to_string()), command: QueryCommands::Program { - command: crate::cli::commands::query::LeoProgram { + command: query::LeoProgram { name: "credits".to_string(), mappings: false, mapping_value: Some(vec!["account".to_string(), address.to_string()]), @@ -276,6 +278,30 @@ fn check_balance( } } +// A helper function to query for the latest block height. +fn get_latest_block_height(endpoint: &str, network: &str, context: &Context) -> Result { + // Query the latest block height. + let height = LeoQuery { + endpoint: Some(endpoint.to_string()), + network: Some(network.to_string()), + command: QueryCommands::Block { + command: query::LeoBlock { + id: None, + latest: false, + latest_hash: false, + latest_height: true, + range: None, + transactions: false, + to_height: false, + }, + }, + } + .execute(Context::new(context.path.clone(), context.home.clone(), true)?)?; + // Parse the height. + let height = height.parse::().map_err(CliError::string_parse_error)?; + Ok(height) +} + /// Determine if the transaction should be broadcast or displayed to user. fn handle_broadcast(endpoint: &String, transaction: Transaction, operation: &String) -> Result<()> { println!("Broadcasting transaction to {}...\n", endpoint.clone()); diff --git a/leo/cli/commands/query/mod.rs b/leo/cli/commands/query/mod.rs index f09766a140..7b99ff81a4 100644 --- a/leo/cli/commands/query/mod.rs +++ b/leo/cli/commands/query/mod.rs @@ -17,26 +17,26 @@ use super::*; use snarkvm::prelude::{CanaryV0, MainnetV0, TestnetV0}; -mod block; -use block::LeoBlock; +pub mod block; +pub use block::LeoBlock; pub mod program; pub use program::LeoProgram; -mod state_root; -use state_root::StateRoot; +pub mod state_root; +pub use state_root::StateRoot; -mod committee; -use committee::LeoCommittee; +pub mod committee; +pub use committee::LeoCommittee; -mod mempool; -use mempool::LeoMempool; +pub mod mempool; +pub use mempool::LeoMempool; -mod peers; -use peers::LeoPeers; +pub mod peers; +pub use peers::LeoPeers; -mod transaction; -use transaction::LeoTransaction; +pub mod transaction; +pub use transaction::LeoTransaction; mod utils; use utils::*; From fc882c1abfcbca375255df48eca31b60b97e1bce Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Sun, 8 Dec 2024 21:11:51 -0800 Subject: [PATCH 2/4] Better defaults --- leo/cli/commands/execute.rs | 21 +++++++++++++-------- leo/cli/commands/mod.rs | 4 ++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs index e09cde6e64..60f6b8540e 100644 --- a/leo/cli/commands/execute.rs +++ b/leo/cli/commands/execute.rs @@ -224,16 +224,21 @@ fn handle_execute( } // Otherwise, default to the one provided in `fee_options`. else { - println!( - "Failed to get the latest block height. Defaulting to V{}.", - command.fee_options.consensus_version - ); - match command.fee_options.consensus_version { - 1 => execution_cost_v1(&vm.process().read(), execution)?, - 2 => execution_cost_v2(&vm.process().read(), execution)?, - version => { + // Get the consensus version from the command. + let version = match command.fee_options.consensus_version { + Some(1) => 1, + None | Some(2) => 2, + Some(version) => { panic!("Invalid consensus version: {version}. Please specify a valid version.") } + }; + // Print a warning message. + println!("Failed to get the latest block height. Defaulting to V{version}.",); + // Use the provided version. + match version { + 1 => execution_cost_v1(&vm.process().read(), execution)?, + 2 => execution_cost_v2(&vm.process().read(), execution)?, + _ => unreachable!(), } } } else { diff --git a/leo/cli/commands/mod.rs b/leo/cli/commands/mod.rs index 3cabd207d3..4bd652f9dc 100644 --- a/leo/cli/commands/mod.rs +++ b/leo/cli/commands/mod.rs @@ -220,8 +220,8 @@ pub struct FeeOptions { long )] record: Option, - #[clap(long, help = "Consensus version to use for the transaction.", default_value = "2")] - pub(crate) consensus_version: u8, + #[clap(long, help = "Consensus version to use for the transaction.")] + pub(crate) consensus_version: Option, } /// Parses the record string. If the string is a ciphertext, then attempt to decrypt it. Lifted from snarkOS. From 582d3b185a83ccc2ee1b1d53926bd408b7361e7c Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:34:06 -0500 Subject: [PATCH 3/4] Allow base_fee option in CLI --- leo/cli/commands/deploy.rs | 49 +++++++++++------- leo/cli/commands/execute.rs | 99 ++++++++++++++++++++----------------- leo/cli/commands/mod.rs | 2 + 3 files changed, 87 insertions(+), 63 deletions(-) diff --git a/leo/cli/commands/deploy.rs b/leo/cli/commands/deploy.rs index 74ca27dd09..be7c3a442a 100644 --- a/leo/cli/commands/deploy.rs +++ b/leo/cli/commands/deploy.rs @@ -160,19 +160,24 @@ fn handle_deploy, N: Network>( // Initialize the VM. let vm = VM::from(store)?; - // Compute the minimum deployment cost. - let (mut total_cost, (storage_cost, synthesis_cost, namespace_cost)) = deployment_cost(&deployment)?; - - // Display the deployment cost breakdown using `credit` denomination. - total_cost += command.fee_options.priority_fee; - 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, - command.fee_options.priority_fee as f64 / 1_000_000.0, - )?; + let base_fee = match command.fee_options.base_fee { + Some(base_fee) => base_fee, + None => { + // Compute the minimum deployment cost. + let (base_fee, (storage_cost, synthesis_cost, namespace_cost)) = deployment_cost(&deployment)?; + + // Display the deployment cost breakdown using `credit` denomination. + deploy_cost_breakdown( + name, + base_fee 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, + command.fee_options.priority_fee as f64 / 1_000_000.0, + )?; + base_fee + } + }; // Initialize an RNG. let rng = &mut rand::thread_rng(); @@ -184,7 +189,7 @@ fn handle_deploy, N: Network>( let fee_authorization = vm.authorize_fee_private( &private_key, fee_record, - total_cost, + base_fee, command.fee_options.priority_fee, deployment_id, rng, @@ -193,10 +198,16 @@ fn handle_deploy, N: Network>( } None => { // Make sure the user has enough public balance to pay for the deployment. - check_balance(&private_key, endpoint, &network.to_string(), &context, total_cost)?; + check_balance( + &private_key, + endpoint, + &network.to_string(), + &context, + base_fee + command.fee_options.priority_fee, + )?; let fee_authorization = vm.authorize_fee_public( &private_key, - total_cost, + base_fee, command.fee_options.priority_fee, deployment_id, rng, @@ -247,13 +258,13 @@ fn handle_deploy, N: Network>( // A helper function to display a cost breakdown of the deployment. fn deploy_cost_breakdown( name: &String, - total_cost: f64, + base_fee: f64, storage_cost: f64, synthesis_cost: f64, namespace_cost: f64, priority_fee: f64, ) -> Result<()> { - println!("\nBase deployment cost for '{}' is {} credits.\n", name.bold(), total_cost); + println!("\nBase deployment cost for '{}' is {} credits.\n", name.bold(), base_fee); // Display the cost breakdown in a table. let data = [ [name, "Cost (credits)"], @@ -261,7 +272,7 @@ fn deploy_cost_breakdown( ["Program Synthesis", &format!("{:.6}", synthesis_cost)], ["Namespace", &format!("{:.6}", namespace_cost)], ["Priority Fee", &format!("{:.6}", priority_fee)], - ["Total", &format!("{:.6}", total_cost)], + ["Total", &format!("{:.6}", base_fee + priority_fee)], ]; let mut out = Vec::new(); text_tables::render(&mut out, data).map_err(CliError::table_render_failed)?; diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs index 60f6b8540e..000a133535 100644 --- a/leo/cli/commands/execute.rs +++ b/leo/cli/commands/execute.rs @@ -212,52 +212,63 @@ fn handle_execute( )?; // Check the transaction cost. - let (mut total_cost, (storage_cost, finalize_cost)) = if let ExecuteTransaction(_, execution, _) = &transaction - { - // Attempt to get the height of the latest block to determine which version of the execution cost to use. - if let Ok(height) = get_latest_block_height(endpoint, &network.to_string(), &context) { - if height < A::Network::CONSENSUS_V2_HEIGHT { - execution_cost_v1(&vm.process().read(), execution)? - } else { - execution_cost_v2(&vm.process().read(), execution)? - } - } - // Otherwise, default to the one provided in `fee_options`. - else { - // Get the consensus version from the command. - let version = match command.fee_options.consensus_version { - Some(1) => 1, - None | Some(2) => 2, - Some(version) => { - panic!("Invalid consensus version: {version}. Please specify a valid version.") - } - }; - // Print a warning message. - println!("Failed to get the latest block height. Defaulting to V{version}.",); - // Use the provided version. - match version { - 1 => execution_cost_v1(&vm.process().read(), execution)?, - 2 => execution_cost_v2(&vm.process().read(), execution)?, - _ => unreachable!(), - } + let base_fee = match command.fee_options.base_fee { + Some(base_fee) => base_fee, + None => { + let (base_fee, (storage_cost, finalize_cost)) = + if let ExecuteTransaction(_, execution, _) = &transaction { + // Attempt to get the height of the latest block to determine which version of the execution cost to use. + if let Ok(height) = get_latest_block_height(endpoint, &network.to_string(), &context) { + if height < A::Network::CONSENSUS_V2_HEIGHT { + execution_cost_v1(&vm.process().read(), execution)? + } else { + execution_cost_v2(&vm.process().read(), execution)? + } + } + // Otherwise, default to the one provided in `fee_options`. + else { + // Get the consensus version from the command. + let version = match command.fee_options.consensus_version { + Some(1) => 1, + None | Some(2) => 2, + Some(version) => { + panic!("Invalid consensus version: {version}. Please specify a valid version.") + } + }; + // Print a warning message. + println!("Failed to get the latest block height. Defaulting to V{version}.",); + // Use the provided version. + match version { + 1 => execution_cost_v1(&vm.process().read(), execution)?, + 2 => execution_cost_v2(&vm.process().read(), execution)?, + _ => unreachable!(), + } + } + } else { + panic!("All transactions should be of type Execute.") + }; + + // Print the cost breakdown. + execution_cost_breakdown( + &program_name, + base_fee as f64 / 1_000_000.0, + storage_cost as f64 / 1_000_000.0, + finalize_cost as f64 / 1_000_000.0, + command.fee_options.priority_fee as f64 / 1_000_000.0, + )?; + base_fee } - } else { - panic!("All transactions should be of type Execute.") }; - // Print the cost breakdown. - total_cost += command.fee_options.priority_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, - command.fee_options.priority_fee as f64 / 1_000_000.0, - )?; - // Check if the public balance is sufficient. if fee_record.is_none() { - check_balance::(&private_key, endpoint, &network.to_string(), &context, total_cost)?; + check_balance::( + &private_key, + endpoint, + &network.to_string(), + &context, + base_fee + command.fee_options.priority_fee, + )?; } // Broadcast the execution transaction. @@ -423,19 +434,19 @@ fn load_program_from_network( // A helper function to display a cost breakdown of the execution. fn execution_cost_breakdown( name: &String, - total_cost: f64, + base_fee: f64, storage_cost: f64, finalize_cost: f64, priority_fee: f64, ) -> Result<()> { - println!("\nBase execution cost for '{}' is {} credits.\n", name.bold(), total_cost); + println!("\nBase execution cost for '{}' is {} credits.\n", name.bold(), base_fee); // Display the cost breakdown in a table. let data = [ [name, "Cost (credits)"], ["Transaction Storage", &format!("{:.6}", storage_cost)], ["On-chain Execution", &format!("{:.6}", finalize_cost)], ["Priority Fee", &format!("{:.6}", priority_fee)], - ["Total", &format!("{:.6}", total_cost)], + ["Total", &format!("{:.6}", base_fee + priority_fee)], ]; let mut out = Vec::new(); text_tables::render(&mut out, data).map_err(CliError::table_render_failed)?; diff --git a/leo/cli/commands/mod.rs b/leo/cli/commands/mod.rs index 4bd652f9dc..5d0e72551f 100644 --- a/leo/cli/commands/mod.rs +++ b/leo/cli/commands/mod.rs @@ -210,6 +210,8 @@ pub struct FeeOptions { pub(crate) yes: bool, #[clap(long, help = "Performs a dry-run of transaction generation")] pub(crate) dry_run: bool, + #[clap(long, help = "Base fee in microcredits. Automatically calculated if not provided.")] + pub(crate) base_fee: Option, #[clap(long, help = "Priority fee in microcredits. Defaults to 0.", default_value = "0")] pub(crate) priority_fee: u64, #[clap(long, help = "Private key to authorize fee expenditure.")] From 26234fd7e2bae57dfd50cc74f79f0de7e2e707ce Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:00:02 -0500 Subject: [PATCH 4/4] Fix leo execution CLI --- leo/cli/commands/execute.rs | 105 ++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/leo/cli/commands/execute.rs b/leo/cli/commands/execute.rs index 000a133535..bc9636e867 100644 --- a/leo/cli/commands/execute.rs +++ b/leo/cli/commands/execute.rs @@ -31,7 +31,6 @@ use snarkvm::circuit::{AleoCanaryV0, AleoV0}; use snarkvm::{ circuit::{Aleo, AleoTestnetV0}, cli::LOCALE, - ledger::Transaction::Execute as ExecuteTransaction, package::Package as SnarkVMPackage, prelude::{ Identifier, @@ -194,58 +193,55 @@ fn handle_execute( let program_id = &ProgramID::::from_str(&format!("{program_name}.aleo"))?; load_program_from_network(context.clone(), &mut vm.process().write(), program_id, network, endpoint)?; + // Compute the authorization. + let authorization = vm.authorize(&private_key, program_id, &command.name, inputs, rng)?; + // Determine if a fee is required. + let is_fee_required = !authorization.is_split(); + // Determine if a priority fee is declared. + let is_priority_fee_declared = command.fee_options.priority_fee > 0; + // Compute the execution. + let execution = match vm.execute_authorization(authorization, None, Some(query.clone()), rng)? { + Transaction::Execute(_, execution, _) => execution, + _ => unreachable!("VM::execute_authorization should return a Transaction::Execute"), + }; + 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.clone()), - inputs.iter(), - fee_record.clone(), - command.fee_options.priority_fee, - Some(query), - rng, - )?; - // Check the transaction cost. let base_fee = match command.fee_options.base_fee { Some(base_fee) => base_fee, None => { let (base_fee, (storage_cost, finalize_cost)) = - if let ExecuteTransaction(_, execution, _) = &transaction { - // Attempt to get the height of the latest block to determine which version of the execution cost to use. - if let Ok(height) = get_latest_block_height(endpoint, &network.to_string(), &context) { - if height < A::Network::CONSENSUS_V2_HEIGHT { - execution_cost_v1(&vm.process().read(), execution)? - } else { - execution_cost_v2(&vm.process().read(), execution)? - } + // Attempt to get the height of the latest block to determine which version of the execution cost to use. + if let Ok(height) = get_latest_block_height(endpoint, &network.to_string(), &context) { + if height < A::Network::CONSENSUS_V2_HEIGHT { + execution_cost_v1(&vm.process().read(), &execution)? + } else { + execution_cost_v2(&vm.process().read(), &execution)? } - // Otherwise, default to the one provided in `fee_options`. - else { - // Get the consensus version from the command. - let version = match command.fee_options.consensus_version { - Some(1) => 1, - None | Some(2) => 2, - Some(version) => { - panic!("Invalid consensus version: {version}. Please specify a valid version.") - } - }; - // Print a warning message. - println!("Failed to get the latest block height. Defaulting to V{version}.",); - // Use the provided version. - match version { - 1 => execution_cost_v1(&vm.process().read(), execution)?, - 2 => execution_cost_v2(&vm.process().read(), execution)?, - _ => unreachable!(), + } + // Otherwise, default to the one provided in `fee_options`. + else { + // Get the consensus version from the command. + let version = match command.fee_options.consensus_version { + Some(1) => 1, + None | Some(2) => 2, + Some(version) => { + panic!("Invalid consensus version: {version}. Please specify a valid version.") } + }; + // Print a warning message. + println!("Failed to get the latest block height. Defaulting to V{version}.",); + // Use the provided version. + match version { + 1 => execution_cost_v1(&vm.process().read(), &execution)?, + 2 => execution_cost_v2(&vm.process().read(), &execution)?, + _ => unreachable!(), } - } else { - panic!("All transactions should be of type Execute.") }; // Print the cost breakdown. @@ -271,6 +267,37 @@ fn handle_execute( )?; } + // Compute the fee. + let fee = match is_fee_required || is_priority_fee_declared { + true => { + // Compute the execution ID. + let execution_id = execution.to_execution_id()?; + // Authorize the fee. + let authorization = match fee_record { + Some(record) => vm.authorize_fee_private( + &private_key, + record, + base_fee, + command.fee_options.priority_fee, + execution_id, + rng, + )?, + None => vm.authorize_fee_public( + &private_key, + base_fee, + command.fee_options.priority_fee, + execution_id, + rng, + )?, + }; + // Execute the fee. + Some(vm.execute_fee_authorization(authorization, Some(query), rng)?) + } + false => None, + }; + // Return the execute transaction. + let transaction = Transaction::from_execution(execution, fee)?; + // Broadcast the execution transaction. if !command.fee_options.dry_run { if !command.fee_options.yes {