Skip to content
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
8 changes: 7 additions & 1 deletion console/network/src/consensus_heights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub enum ConsensusVersion {
V10 = 10,
/// V11: Support for external structs.
V11 = 11,
/// V12: Support for block-level deployment constraints and variables limits.
V12 = 12,
}

impl ConsensusVersion {
Expand All @@ -50,7 +52,7 @@ impl ConsensusVersion {
}

/// The number of consensus versions.
pub(crate) const NUM_CONSENSUS_VERSIONS: usize = 11;
pub(crate) const NUM_CONSENSUS_VERSIONS: usize = 12;

/// The consensus version height for `CanaryV0`.
pub const CANARY_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
Expand All @@ -65,6 +67,7 @@ pub const CANARY_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CON
(ConsensusVersion::V9, 8_028_000),
(ConsensusVersion::V10, 8_600_000),
(ConsensusVersion::V11, 10_235_000),
(ConsensusVersion::V12, 11_800_000),
];

/// The consensus version height for `MainnetV0`.
Expand All @@ -80,6 +83,7 @@ pub const MAINNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CO
(ConsensusVersion::V9, 10_272_000),
(ConsensusVersion::V10, 11_205_000),
(ConsensusVersion::V11, 13_575_000),
(ConsensusVersion::V12, 15_200_000),
];

/// The consensus version heights for `TestnetV0`.
Expand All @@ -95,6 +99,7 @@ pub const TESTNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CO
(ConsensusVersion::V9, 9_800_000),
(ConsensusVersion::V10, 10_525_000),
(ConsensusVersion::V11, 12_660_000),
(ConsensusVersion::V12, 14_300_000),
];

/// The consensus version heights when the `test_consensus_heights` feature is enabled.
Expand All @@ -110,6 +115,7 @@ pub const TEST_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSU
(ConsensusVersion::V9, 12),
(ConsensusVersion::V10, 13),
(ConsensusVersion::V11, 14),
(ConsensusVersion::V12, 15),
];

#[cfg(any(test, feature = "test", feature = "test_consensus_heights"))]
Expand Down
16 changes: 12 additions & 4 deletions console/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,18 @@ pub trait Network:
const EXECUTION_STORAGE_PENALTY_THRESHOLD: u64 = 5000;
/// The cost in microcredits per constraint for the deployment transaction.
const SYNTHESIS_FEE_MULTIPLIER: u64 = 25; // 25 microcredits per constraint
/// The maximum number of variables in a deployment.
const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 21; // 2,097,152 variables
/// The maximum number of constraints in a deployment.
const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 21; // 2,097,152 constraints
/// The maximum number of variables in a deployment. These limit were enforced at the transaction level.
/// This corresponds to ~0.5 second single-threaded runtime at mainnet launch reference validator hardware.
const MAX_DEPLOYMENT_VARIABLES_V0: u64 = 1 << 21; // 2,097,152 variables
/// The maximum number of constraints in a deployment. These limit were enforced at the transaction level.
/// This corresponds to ~0.5 second single-threaded runtime at mainnet launch reference validator hardware.
const MAX_DEPLOYMENT_CONSTRAINTS_V0: u64 = 1 << 21; // 2,097,152 constraints
/// The maximum number of variables in a deployment. These limits are enforced at the block level.
/// This corresponds to ~4 seconds single-threaded runtime at mainnet launch reference validator hardware.
const MAX_DEPLOYMENT_VARIABLES_V1: u64 = 1 << 24; // 16,777,216 variables
/// The maximum number of constraints in a deployment. These limits are enforced at the block level.
/// This corresponds to ~4 seconds single-threaded runtime at mainnet launch reference validator hardware.
const MAX_DEPLOYMENT_CONSTRAINTS_V1: u64 = 1 << 24; // 16,777,216 constraints
/// The maximum number of microcredits that can be spent as a fee.
const MAX_FEE: u64 = 1_000_000_000_000_000;
/// A list of consensus versions and their corresponding transaction spend limits in microcredits.
Expand Down
22 changes: 17 additions & 5 deletions ledger/narwhal/batch-header/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,17 +351,29 @@ mod tests {

#[test]
fn test_max_synthesis_cost_below_batch_spend_limit() {
fn max_synthesis_cost_valid<N: Network>() {
let max_synthesis_cost = N::MAX_DEPLOYMENT_VARIABLES.saturating_add(N::MAX_DEPLOYMENT_CONSTRAINTS)
fn max_synthesis_cost_valid_v0<N: Network>() {
let max_synthesis_cost = N::MAX_DEPLOYMENT_VARIABLES_V0.saturating_add(N::MAX_DEPLOYMENT_CONSTRAINTS_V0)
* N::SYNTHESIS_FEE_MULTIPLIER
/ N::ARC_0005_COMPUTE_DISCOUNT;
for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
assert!(max_synthesis_cost < BatchHeader::<N>::batch_spend_limit(*height));
}
}

max_synthesis_cost_valid::<CanaryV0>();
max_synthesis_cost_valid::<TestnetV0>();
max_synthesis_cost_valid::<MainnetV0>();
fn max_synthesis_cost_valid_v1<N: Network>() {
let max_synthesis_cost = N::MAX_DEPLOYMENT_VARIABLES_V1.saturating_add(N::MAX_DEPLOYMENT_CONSTRAINTS_V1)
* N::SYNTHESIS_FEE_MULTIPLIER
/ N::ARC_0005_COMPUTE_DISCOUNT;
for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
assert!(max_synthesis_cost < BatchHeader::<N>::batch_spend_limit(*height));
}
}

max_synthesis_cost_valid_v0::<CanaryV0>();
max_synthesis_cost_valid_v0::<TestnetV0>();
max_synthesis_cost_valid_v0::<MainnetV0>();
max_synthesis_cost_valid_v1::<CanaryV0>();
max_synthesis_cost_valid_v1::<TestnetV0>();
max_synthesis_cost_valid_v1::<MainnetV0>();
}
}
12 changes: 7 additions & 5 deletions synthesizer/process/src/stack/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl<N: Network> Stack<N> {
#[inline]
pub fn verify_deployment<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
&self,
_consensus_version: ConsensusVersion,
consensus_version: ConsensusVersion,
deployment: &Deployment<N>,
rng: &mut R,
) -> Result<()> {
Expand All @@ -83,10 +83,12 @@ impl<N: Network> Stack<N> {
// Get the program ID.
let program_id = self.program.id();

// Check that the number of combined variables does not exceed the deployment limit.
ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES);
// Check that the number of combined constraints does not exceed the deployment limit.
ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS);
if consensus_version <= ConsensusVersion::V12 {
// Check that the number of combined variables does not exceed the deployment limit.
ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES_V0);
// Check that the number of combined constraints does not exceed the deployment limit.
ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS_V0);
}

// Construct the call stacks and assignments used to verify the certificates.
let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len());
Expand Down
56 changes: 54 additions & 2 deletions synthesizer/src/vm/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
process.revert_stacks();
}

// Determine the consensus version.
let consensus_version = N::CONSENSUS_VERSION(state.block_height()).unwrap();

// Initialize a list of the confirmed transactions.
let mut confirmed = Vec::with_capacity(num_transactions);
// Initialize a list of the aborted transactions.
Expand All @@ -305,6 +308,10 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
let mut deployment_payers: IndexSet<Address<N>> = IndexSet::new();
// Initialize a list of the successful deployments.
let mut deployments = IndexSet::new();
// Initialize a counter for the deployment constraints seen.
let mut deployment_constraints_seen = 0u64;
// Initialize a counter for the deployment variables seen.
let mut deployment_variables_seen = 0u64;

// Finalize the transactions.
'outer: for transaction in transactions {
Expand All @@ -319,13 +326,16 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {

// Determine if the transaction should be aborted.
if let Some(reason) = self.should_abort_transaction(
consensus_version,
transaction,
&transition_ids,
&input_ids,
&output_ids,
&tpks,
&deployment_payers,
&deployments,
deployment_constraints_seen,
deployment_variables_seen,
) {
// Store the aborted transaction.
aborted.push((transaction.clone(), reason));
Expand Down Expand Up @@ -473,6 +483,10 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
if let Transaction::Deploy(_, _, _, deployment, fee) = confirmed_transaction.transaction() {
fee.payer().map(|payer| deployment_payers.insert(payer));
deployments.insert(*deployment.program_id());
deployment_constraints_seen = deployment_constraints_seen
.saturating_add(deployment.num_combined_constraints().map_err(|e| e.to_string())?);
deployment_variables_seen = deployment_variables_seen
.saturating_add(deployment.num_combined_variables().map_err(|e| e.to_string())?);
}
// Store the confirmed transaction.
confirmed.push(confirmed_transaction);
Expand Down Expand Up @@ -807,19 +821,23 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
/// - The transaction is producing a duplicate transition public key
/// - The transaction is another deployment in the block from the same public fee payer.
/// - The transaction contains a transition that has been deployed or upgraded in this block.
/// - The transaction is exceeding the deployment constraints or variables limit.
///
/// - Note: If a transaction is a deployment for a program following its deployment or redeployment in this block,
/// it is not aborted. Instead, it will be rejected and its fee will be consumed.
#[allow(clippy::too_many_arguments)]
fn should_abort_transaction(
&self,
consensus_version: ConsensusVersion,
transaction: &Transaction<N>,
transition_ids: &IndexSet<N::TransitionID>,
input_ids: &IndexSet<Field<N>>,
output_ids: &IndexSet<Field<N>>,
tpks: &IndexSet<Group<N>>,
deployment_payers: &IndexSet<Address<N>>,
deployments: &IndexSet<ProgramID<N>>,
deployments_seen: &IndexSet<ProgramID<N>>,
deployment_constraints_seen: u64,
deployment_variables_seen: u64,
) -> Option<String> {
// Ensure that:
// - the transaction is not producing a duplicate transition.
Expand All @@ -834,7 +852,7 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
return Some(format!("Duplicate transition {transition_id}"));
}
// If the transition's program is being deployed or redeployed in this block, abort the transaction.
if deployments.contains(transition.program_id()) {
if deployments_seen.contains(transition.program_id()) {
return Some(format!(
"Program {} is being deployed or redeployed in this block",
transition.program_id()
Expand Down Expand Up @@ -877,6 +895,26 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
}
}

if consensus_version >= ConsensusVersion::V12 {
// If the transaction is a deployment, ensure that it is not exceeding the deployment constraints or variables limit.
if let Transaction::Deploy(_, _, _, deployment, _) = transaction {
let Ok(num_combined_constraints) = deployment.num_combined_constraints() else {
return Some("Failed to compute the number of combined constraints for a deployment".to_string());
};
let num_combined_constraints = num_combined_constraints.saturating_add(deployment_constraints_seen);
let Ok(num_combined_variables) = deployment.num_combined_variables() else {
return Some("Failed to compute the number of combined variables for a deployment".to_string());
};
let num_combined_variables = num_combined_variables.saturating_add(deployment_variables_seen);
// If the deployment constraints or variables are already seen, abort the transaction.
if num_combined_constraints >= N::MAX_DEPLOYMENT_CONSTRAINTS_V1
|| num_combined_variables >= N::MAX_DEPLOYMENT_VARIABLES_V1
{
return Some("Exceeding deployment constraints or variables limit".to_string());
}
}
}

// Return `None` because the transaction is well-formed.
None
}
Expand All @@ -897,6 +935,9 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
let mut valid_transactions = Vec::with_capacity(transactions.len());
let mut aborted_transactions = Vec::with_capacity(transactions.len());

// Determine the consensus version.
let consensus_version = N::CONSENSUS_VERSION(self.block_store().current_block_height())?;

// Initialize a list of created transition IDs.
let mut transition_ids: IndexSet<N::TransitionID> = Default::default();
// Initialize a list of spent input IDs.
Expand All @@ -909,6 +950,10 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
let mut deployment_payers: IndexSet<Address<N>> = Default::default();
// Initialize a list of the successful deployments.
let mut deployments = IndexSet::new();
// Initialize a counter for the deployment constraints seen.
let mut deployment_constraints_seen = 0u64;
// Initialize a counter for the deployment variables seen.
let mut deployment_variables_seen = 0u64;

// Abort the transactions that are have duplicates or are invalid. This will prevent the VM from performing
// verification on transactions that would have been aborted in `VM::atomic_speculate`.
Expand All @@ -921,13 +966,16 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {

// Determine if the transaction should be aborted.
match self.should_abort_transaction(
consensus_version,
transaction,
&transition_ids,
&input_ids,
&output_ids,
&tpks,
&deployment_payers,
&deployments,
deployment_constraints_seen,
deployment_variables_seen,
) {
// Store the aborted transaction.
Some(reason) => aborted_transactions.push((*transaction, reason.to_string())),
Expand All @@ -945,6 +993,10 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
if let Transaction::Deploy(_, _, _, deployment, fee) = transaction {
fee.payer().map(|payer| deployment_payers.insert(payer));
deployments.insert(*deployment.program_id());
deployment_constraints_seen =
deployment_constraints_seen.saturating_add(deployment.num_combined_constraints()?);
deployment_variables_seen =
deployment_variables_seen.saturating_add(deployment.num_combined_variables()?);
}

// Add the transaction to the list of transactions to verify.
Expand Down