Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Select fee based on consensus height #28473

Merged
merged 4 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions leo/cli/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,24 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, 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();
Expand All @@ -184,7 +189,7 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, 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,
Expand All @@ -193,10 +198,16 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, 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,
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,
Expand Down Expand Up @@ -247,21 +258,21 @@ fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, 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)"],
["Transaction Storage", &format!("{:.6}", storage_cost)],
["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)?;
Expand Down
128 changes: 96 additions & 32 deletions leo/cli/commands/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -42,6 +41,7 @@ use snarkvm::{
VM,
Value,
execution_cost_v1,
execution_cost_v2,
query::Query as SnarkVMQuery,
store::{
ConsensusStore,
Expand Down Expand Up @@ -193,47 +193,111 @@ fn handle_execute<A: Aleo>(
let program_id = &ProgramID::<A::Network>::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 (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)?
} else {
panic!("All transactions should be of type Execute.")
let base_fee = match command.fee_options.base_fee {
Some(base_fee) => base_fee,
None => {
let (base_fee, (storage_cost, finalize_cost)) =
// 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!(),
}
};

// 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
}
};

// 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::<A::Network>(&private_key, endpoint, &network.to_string(), context, total_cost)?;
check_balance::<A::Network>(
&private_key,
endpoint,
&network.to_string(),
&context,
base_fee + command.fee_options.priority_fee,
)?;
}

// 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 {
Expand Down Expand Up @@ -397,19 +461,19 @@ 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,
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)?;
Expand Down
32 changes: 30 additions & 2 deletions leo/cli/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>,
#[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.")]
Expand All @@ -220,6 +222,8 @@ pub struct FeeOptions {
long
)]
record: Option<String>,
#[clap(long, help = "Consensus version to use for the transaction.")]
pub(crate) consensus_version: Option<u8>,
}

/// Parses the record string. If the string is a ciphertext, then attempt to decrypt it. Lifted from snarkOS.
Expand All @@ -241,7 +245,7 @@ fn check_balance<N: Network>(
private_key: &PrivateKey<N>,
endpoint: &str,
network: &str,
context: Context,
context: &Context,
total_cost: u64,
) -> Result<()> {
// Derive the account address.
Expand All @@ -251,7 +255,7 @@ fn check_balance<N: Network>(
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()]),
Expand All @@ -276,6 +280,30 @@ fn check_balance<N: Network>(
}
}

// A helper function to query for the latest block height.
fn get_latest_block_height(endpoint: &str, network: &str, context: &Context) -> Result<u32> {
// 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::<u32>().map_err(CliError::string_parse_error)?;
Ok(height)
}

/// 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 {}...\n", endpoint.clone());
Expand Down
24 changes: 12 additions & 12 deletions leo/cli/commands/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
Loading