Skip to content

Commit

Permalink
Merge pull request #28473 from ProvableHQ/fix/executions-fees
Browse files Browse the repository at this point in the history
[Fix] Select fee based on consensus height
  • Loading branch information
d0cd authored Dec 17, 2024
2 parents aa6a5f3 + 26234fd commit c7aac4c
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 65 deletions.
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 @@ -213,6 +213,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 @@ -223,6 +225,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 @@ -244,7 +248,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 @@ -254,7 +258,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 @@ -279,6 +283,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

0 comments on commit c7aac4c

Please sign in to comment.