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

[Feature] Batch proposal spend limits #2601

Draft
wants to merge 6 commits into
base: staging
Choose a base branch
from
Draft
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
12 changes: 8 additions & 4 deletions console/network/src/canary_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,17 @@ impl Network for CanaryV0 {
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(not(any(test, feature = "test")))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 2_900_000), (ConsensusVersion::V3, 4_560_000)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] = [
(ConsensusVersion::V1, 0),
(ConsensusVersion::V2, 2_900_000),
(ConsensusVersion::V3, 4_560_000),
(ConsensusVersion::V4, 5_000_000),
];
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(any(test, feature = "test"))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11), (ConsensusVersion::V4, 12)];
/// The network edition.
const EDITION: u16 = 0;
/// The genesis block coinbase target.
Expand Down
9 changes: 8 additions & 1 deletion console/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub enum ConsensusVersion {
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
}

pub trait Network:
Expand Down Expand Up @@ -132,6 +133,12 @@ pub trait Network:
const MAX_FEE: u64 = 1_000_000_000_000_000;
/// The maximum number of microcredits that can be spent on a finalize block.
const TRANSACTION_SPEND_LIMIT: u64 = 100_000_000;
/// The maximum number of microcredits that can be spent by the transactions in a batch.
/// This implies the block spend limit is bounded at `TRANSACTION_SPEND_LIMIT * N::NUM_MAX_CERTIFICATES`.
const BATCH_SPEND_LIMIT: u64 = Self::TRANSACTION_SPEND_LIMIT;
/// The base cost in microcredits to verify an execution.
/// NOTE: this constant reflects the compute cost of an execution, but is not required to be paid by the user.
const EXECUTION_BASE_COST: u64 = 2_000_000; // 2 million microcredits.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"is not required to be paid by the user" What does this mean?

A transfer_public is currently around 51k microcredits, why is there a need for the additional 2 million microcredits?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this to arbitrarily say that a proposal is upper bounded by BATCH_SPEND_LIMIT / EXECUTION_BASE_COST number of executions?


/// The anchor height, defined as the expected number of blocks to reach the coinbase target.
const ANCHOR_HEIGHT: u32 = Self::ANCHOR_TIME as u32 / Self::BLOCK_TIME as u32;
Expand Down Expand Up @@ -217,7 +224,7 @@ pub trait Network:

/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4];
/// A list of (consensus_version, size) pairs indicating the maximum number of validators in a committee.
// Note: This value must **not** decrease without considering the impact on serialization.
// Decreasing this value will break backwards compatibility of serialization without explicit
Expand Down
12 changes: 8 additions & 4 deletions console/network/src/mainnet_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,17 @@ impl Network for MainnetV0 {
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(not(any(test, feature = "test")))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 2_800_000), (ConsensusVersion::V3, 4_900_000)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] = [
(ConsensusVersion::V1, 0),
(ConsensusVersion::V2, 2_800_000),
(ConsensusVersion::V3, 4_900_000),
(ConsensusVersion::V4, 5_500_000),
];
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(any(test, feature = "test"))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11), (ConsensusVersion::V4, 12)];
/// The network edition.
const EDITION: u16 = 0;
/// The genesis block coinbase target.
Expand Down
12 changes: 8 additions & 4 deletions console/network/src/testnet_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,17 @@ impl Network for TestnetV0 {
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(not(any(test, feature = "test")))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 2_950_000), (ConsensusVersion::V3, 4_800_000)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] = [
(ConsensusVersion::V1, 0),
(ConsensusVersion::V2, 2_950_000),
(ConsensusVersion::V3, 4_800_000),
(ConsensusVersion::V4, 5_000_000),
];
/// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
/// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
#[cfg(any(test, feature = "test"))]
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 3] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11)];
const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 4] =
[(ConsensusVersion::V1, 0), (ConsensusVersion::V2, 10), (ConsensusVersion::V3, 11), (ConsensusVersion::V4, 12)];
/// The network edition.
const EDITION: u16 = 0;
/// The genesis block coinbase target.
Expand Down
49 changes: 39 additions & 10 deletions synthesizer/process/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use console::{
prelude::*,
program::{FinalizeType, Identifier, LiteralType, PlaintextType},
};
use ledger_block::{Deployment, Execution};
use ledger_block::{Deployment, Execution, Transaction};
use synthesizer_program::{CastType, Command, Finalize, Instruction, Operand, StackProgram};

/// Returns the *minimum* cost in microcredits to publish the given deployment (total cost, (storage cost, synthesis cost, namespace cost)).
Expand All @@ -30,18 +30,14 @@ pub fn deployment_cost<N: Network>(deployment: &Deployment<N>) -> Result<(u64, (
let program_id = deployment.program_id();
// Determine the number of characters in the program ID.
let num_characters = u32::try_from(program_id.name().to_string().len())?;
// Compute the number of combined variables in the program.
let num_combined_variables = deployment.num_combined_variables()?;
// Compute the number of combined constraints in the program.
let num_combined_constraints = deployment.num_combined_constraints()?;

// Compute the storage cost in microcredits.
let storage_cost = size_in_bytes
.checked_mul(N::DEPLOYMENT_FEE_MULTIPLIER)
.ok_or(anyhow!("The storage cost computation overflowed for a deployment"))?;

// Compute the synthesis cost in microcredits.
let synthesis_cost = num_combined_variables.saturating_add(num_combined_constraints) * N::SYNTHESIS_FEE_MULTIPLIER;
let synthesis_cost = deployment_synthesis_cost(deployment)?;

// Compute the namespace cost in credits: 10^(10 - num_characters).
let namespace_cost = 10u64
Expand All @@ -58,6 +54,15 @@ pub fn deployment_cost<N: Network>(deployment: &Deployment<N>) -> Result<(u64, (
Ok((total_cost, (storage_cost, synthesis_cost, namespace_cost)))
}

/// Returns the cost in microcredits to synthesize a deployment.
pub fn deployment_synthesis_cost<N: Network>(deployment: &Deployment<N>) -> Result<u64> {
let num_combined_variables = deployment.num_combined_variables()?;
let num_combined_constraints = deployment.num_combined_constraints()?;
let synthesis_cost = num_combined_variables.saturating_add(num_combined_constraints) * N::SYNTHESIS_FEE_MULTIPLIER;

Ok(synthesis_cost)
}

/// Returns the *minimum* cost in microcredits to publish the given execution (total cost, (storage cost, finalize cost)).
pub fn execution_cost_v2<N: Network>(process: &Process<N>, execution: &Execution<N>) -> Result<(u64, (u64, u64))> {
// Compute the storage cost in microcredits.
Expand Down Expand Up @@ -87,7 +92,7 @@ pub fn execution_cost_v1<N: Network>(process: &Process<N>, execution: &Execution

// Get the finalize cost for the root transition.
let stack = process.get_stack(transition.program_id())?;
let finalize_cost = cost_in_microcredits_v1(stack, transition.function_name())?;
let finalize_cost = finalize_cost_v1(stack, transition.function_name())?;

// Compute the total cost in microcredits.
let total_cost = storage_cost
Expand Down Expand Up @@ -403,7 +408,7 @@ pub fn cost_per_command<N: Network>(
}

/// Returns the minimum number of microcredits required to run the finalize.
pub fn cost_in_microcredits_v2<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
pub fn finalize_cost_v2<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
// Retrieve the finalize logic.
let Some(finalize) = stack.get_function_ref(function_name)?.finalize_logic() else {
// Return a finalize cost of 0, if the function does not have a finalize scope.
Expand Down Expand Up @@ -432,7 +437,7 @@ pub fn cost_in_microcredits_v2<N: Network>(stack: &Stack<N>, function_name: &Ide
}

/// Returns the minimum number of microcredits required to run the finalize (depcrated).
pub fn cost_in_microcredits_v1<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
pub fn finalize_cost_v1<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
// Retrieve the finalize logic.
let Some(finalize) = stack.get_function_ref(function_name)?.finalize_logic() else {
// Return a finalize cost of 0, if the function does not have a finalize scope.
Expand All @@ -446,7 +451,7 @@ pub fn cost_in_microcredits_v1<N: Network>(stack: &Stack<N>, function_name: &Ide
let stack = stack.get_external_stack(future.program_id())?;
// Accumulate the finalize cost of the future.
future_cost = future_cost
.checked_add(cost_in_microcredits_v1(stack, future.resource())?)
.checked_add(finalize_cost_v1(stack, future.resource())?)
.ok_or(anyhow!("Finalize cost overflowed"))?;
}
}
Expand All @@ -460,6 +465,30 @@ pub fn cost_in_microcredits_v1<N: Network>(stack: &Stack<N>, function_name: &Ide
})
}

/// Returns the compute cost for a transaction in microcredits.
/// TODO(nkls): clarify context w.r.t. `PROPOSAL_SPEND_LIMIT`.
/// This does NOT represent the full costs which a user has to pay.
use std::sync::Arc;
pub fn compute_cost<N: Network>(transaction: &Transaction<N>, stack: Option<Arc<Stack<N>>>) -> Result<u64> {
match transaction {
// Synthesis cost accounts for the majority of deployment transaction compute.
Transaction::Deploy(_, _, _, deployment, _) => deployment_synthesis_cost(deployment),
// Base and finalize costs account for the majority of execute transaction compute.
Transaction::Execute(_, _, execution, _) => {
// Get the root transition for the program.
let root_transition = execution.peek()?;
// Check a stack is present for the execution.
let stack = stack.ok_or(anyhow!("Expected a Stack containing the Execution's finalize cost."))?;
// Retrieve the finalize cost for the root program.
let finalize_cost = stack.get_finalize_cost(root_transition.function_name())?;

Ok(finalize_cost.saturating_add(N::EXECUTION_BASE_COST))
}
// Fee transactions are internal to the VM, they do not have a compute cost.
Transaction::Fee(..) => Ok(0),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion synthesizer/process/src/stack/helpers/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<N: Network> Stack<N> {
stack.number_of_calls.insert(*function.name(), num_calls);

// Get the finalize cost.
let finalize_cost = cost_in_microcredits_v2(&stack, function.name())?;
let finalize_cost = finalize_cost_v2(&stack, function.name())?;
// Check that the finalize cost does not exceed the maximum.
ensure!(
finalize_cost <= N::TRANSACTION_SPEND_LIMIT,
Expand Down
2 changes: 1 addition & 1 deletion synthesizer/process/src/stack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod evaluate;
mod execute;
mod helpers;

use crate::{CallMetrics, Process, Trace, cost_in_microcredits_v2, traits::*};
use crate::{CallMetrics, Process, Trace, finalize_cost_v2, traits::*};
use console::{
account::{Address, PrivateKey},
network::prelude::*,
Expand Down