From 23b3343f168e4b3cac3c6a25a409589c950ba41c Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:24:40 -0800 Subject: [PATCH 01/13] Add MAX_CALL_DEPTH and MAX_IMPORT_DEPTH --- console/network/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index aa03a7cdfd..2da742d6ca 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -160,6 +160,11 @@ pub trait Network: /// The maximum number of outputs per transition. const MAX_OUTPUTS: usize = 16; + /// The maximum call depth. + const MAX_CALL_DEPTH: usize = 1024; + /// The maximum import depth. + const MAX_IMPORT_DEPTH: usize = 1024; + /// The state root type. type StateRoot: Bech32ID>; /// The block hash type. From 8e3342955a3949064adb305a4d202ddebde7e931 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:45:48 -0800 Subject: [PATCH 02/13] Add bound on number of imports in a program --- console/network/src/lib.rs | 6 ++---- synthesizer/program/src/lib.rs | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 2da742d6ca..2a570bf44b 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -160,10 +160,8 @@ pub trait Network: /// The maximum number of outputs per transition. const MAX_OUTPUTS: usize = 16; - /// The maximum call depth. - const MAX_CALL_DEPTH: usize = 1024; - /// The maximum import depth. - const MAX_IMPORT_DEPTH: usize = 1024; + /// The maximum number of imports. This is roughly bounded by the `MAX_FUNCTIONS * MAX_TRANSITIONS` + const MAX_IMPORTS: usize = 1024; /// The state root type. type StateRoot: Bech32ID>; diff --git a/synthesizer/program/src/lib.rs b/synthesizer/program/src/lib.rs index 39daab2ceb..9ff50db651 100644 --- a/synthesizer/program/src/lib.rs +++ b/synthesizer/program/src/lib.rs @@ -295,6 +295,9 @@ impl, Command: CommandTrait> Pro // Retrieve the imported program name. let import_name = *import.name(); + // Ensure that the number of imports is within the allowed range. + ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports"); + // Ensure the import name is new. ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use."); // Ensure the import name is not a reserved opcode. From 44aeb089c13d484a37d70c640413a9cd6b1ea028 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:07:26 -0800 Subject: [PATCH 03/13] Cache number of function calls --- .../process/src/stack/helpers/initialize.rs | 19 +++++++++++++++ synthesizer/process/src/stack/mod.rs | 23 +++++-------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/synthesizer/process/src/stack/helpers/initialize.rs b/synthesizer/process/src/stack/helpers/initialize.rs index 46b6bd2465..ba2a29a0e0 100644 --- a/synthesizer/process/src/stack/helpers/initialize.rs +++ b/synthesizer/process/src/stack/helpers/initialize.rs @@ -27,6 +27,7 @@ impl Stack { universal_srs: process.universal_srs().clone(), proving_keys: Default::default(), verifying_keys: Default::default(), + number_of_calls: Default::default(), }; // Add all of the imports into the stack. @@ -49,6 +50,24 @@ impl Stack { for function in program.functions().values() { // Add the function to the stack. stack.insert_function(function)?; + // Determine the number of calls for the function. + let mut num_calls = 1; + for instruction in function.instructions() { + if let Instruction::Call(call) = instruction { + // Determine if this is a function call. + if call.is_function_call(&stack)? { + // Increment by the number of calls. + num_calls += match call.operator() { + CallOperator::Locator(locator) => stack + .get_external_stack(locator.program_id())? + .get_number_of_calls(locator.resource())?, + CallOperator::Resource(resource) => stack.get_number_of_calls(resource)?, + }; + } + } + } + // Add the number of calls to the stack. + stack.number_of_calls.insert(*function.name(), num_calls); } // Return the stack. Ok(stack) diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 8f23e126d8..2e1dc1e9f1 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -185,6 +185,8 @@ pub struct Stack { proving_keys: Arc, ProvingKey>>>, /// The mapping of function name to verifying key. verifying_keys: Arc, VerifyingKey>>>, + /// The mapping of function names to the number of calls. + number_of_calls: IndexMap, usize>, } impl Stack { @@ -279,23 +281,10 @@ impl StackProgram for Stack { /// Returns the expected number of calls for the given function name. #[inline] fn get_number_of_calls(&self, function_name: &Identifier) -> Result { - // Determine the number of calls for this function (including the function itself). - let mut num_calls = 1; - for instruction in self.get_function(function_name)?.instructions() { - if let Instruction::Call(call) = instruction { - // Determine if this is a function call. - if call.is_function_call(self)? { - // Increment by the number of calls. - num_calls += match call.operator() { - CallOperator::Locator(locator) => { - self.get_external_stack(locator.program_id())?.get_number_of_calls(locator.resource())? - } - CallOperator::Resource(resource) => self.get_number_of_calls(resource)?, - }; - } - } - } - Ok(num_calls) + self.number_of_calls + .get(function_name) + .copied() + .ok_or_else(|| anyhow!("Function '{function_name}' does not exist")) } /// Returns a value for the given value type. From bcd21c6bd3602d70d48041668aff3597a911ffc7 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:28:41 -0800 Subject: [PATCH 04/13] Restrict maximum nmber of calls to MAX_TRANSITIONS on Stack::initialize --- ledger/block/src/transaction/merkle.rs | 2 +- synthesizer/process/src/stack/helpers/initialize.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ledger/block/src/transaction/merkle.rs b/ledger/block/src/transaction/merkle.rs index f84ea87ca7..eca22c3b68 100644 --- a/ledger/block/src/transaction/merkle.rs +++ b/ledger/block/src/transaction/merkle.rs @@ -16,7 +16,7 @@ use super::*; impl Transaction { /// The maximum number of transitions allowed in a transaction. - const MAX_TRANSITIONS: usize = usize::pow(2, TRANSACTION_DEPTH as u32); + pub const MAX_TRANSITIONS: usize = usize::pow(2, TRANSACTION_DEPTH as u32); /// Returns the transaction root, by computing the root for a Merkle tree of the transition IDs. pub fn to_root(&self) -> Result> { diff --git a/synthesizer/process/src/stack/helpers/initialize.rs b/synthesizer/process/src/stack/helpers/initialize.rs index ba2a29a0e0..d35b69908c 100644 --- a/synthesizer/process/src/stack/helpers/initialize.rs +++ b/synthesizer/process/src/stack/helpers/initialize.rs @@ -66,6 +66,12 @@ impl Stack { } } } + // Check that the number of calls does not exceed the maximum. + // Note that one transition is reserved for the fee. + ensure!( + num_calls < ledger_block::Transaction::::MAX_TRANSITIONS, + "Number of calls exceeds the maximum allowed number of transitions" + ); // Add the number of calls to the stack. stack.number_of_calls.insert(*function.name(), num_calls); } From adc785981983d8eb82d30ccb36029ccd1b4e43c4 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 12:12:03 -0800 Subject: [PATCH 05/13] Restrict program depth --- console/network/src/lib.rs | 2 + .../process/src/stack/helpers/initialize.rs | 10 +- synthesizer/process/src/stack/mod.rs | 8 ++ synthesizer/process/src/tests/test_execute.rs | 93 ++++++++++++++++++- .../program/src/traits/stack_and_registers.rs | 3 + 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 2a570bf44b..f8ed3a4339 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -162,6 +162,8 @@ pub trait Network: /// The maximum number of imports. This is roughly bounded by the `MAX_FUNCTIONS * MAX_TRANSITIONS` const MAX_IMPORTS: usize = 1024; + /// The maximum program depth. + const MAX_PROGRAM_DEPTH: usize = 1024; /// The state root type. type StateRoot: Bech32ID>; diff --git a/synthesizer/process/src/stack/helpers/initialize.rs b/synthesizer/process/src/stack/helpers/initialize.rs index d35b69908c..7d6d377ba7 100644 --- a/synthesizer/process/src/stack/helpers/initialize.rs +++ b/synthesizer/process/src/stack/helpers/initialize.rs @@ -28,6 +28,7 @@ impl Stack { proving_keys: Default::default(), verifying_keys: Default::default(), number_of_calls: Default::default(), + program_depth: 0, }; // Add all of the imports into the stack. @@ -40,12 +41,19 @@ impl Stack { let external_stack = process.get_stack(import)?; // Add the external stack to the stack. stack.insert_external_stack(external_stack.clone())?; + // Update the program depth, checking that it does not exceed the maximum call depth. + stack.program_depth = std::cmp::max(stack.program_depth, external_stack.program_depth() + 1); + ensure!( + stack.program_depth <= N::MAX_PROGRAM_DEPTH, + "Program depth exceeds the maximum allowed call depth" + ); } // Add the program closures to the stack. for closure in program.closures().values() { // Add the closure to the stack. stack.insert_closure(closure)?; } + // Add the program functions to the stack. for function in program.functions().values() { // Add the function to the stack. @@ -75,6 +83,7 @@ impl Stack { // Add the number of calls to the stack. stack.number_of_calls.insert(*function.name(), num_calls); } + // Return the stack. Ok(stack) } @@ -134,7 +143,6 @@ impl Stack { // Add the finalize name and finalize types to the stack. self.finalize_types.insert(*name, finalize_types); } - // Return success. Ok(()) } diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 2e1dc1e9f1..852857fe4e 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -187,6 +187,8 @@ pub struct Stack { verifying_keys: Arc, VerifyingKey>>>, /// The mapping of function names to the number of calls. number_of_calls: IndexMap, usize>, + /// The program depth. + program_depth: usize, } impl Stack { @@ -222,6 +224,12 @@ impl StackProgram for Stack { &self.program } + /// Returns the program depth. + #[inline] + fn program_depth(&self) -> usize { + self.program_depth + } + /// Returns the program ID. #[inline] fn program_id(&self) -> &ProgramID { diff --git a/synthesizer/process/src/tests/test_execute.rs b/synthesizer/process/src/tests/test_execute.rs index 61b80a33ff..808cf36102 100644 --- a/synthesizer/process/src/tests/test_execute.rs +++ b/synthesizer/process/src/tests/test_execute.rs @@ -34,7 +34,7 @@ use ledger_store::{ FinalizeStorage, FinalizeStore, }; -use synthesizer_program::{FinalizeGlobalState, FinalizeStoreTrait, Program}; +use synthesizer_program::{FinalizeGlobalState, FinalizeStoreTrait, Program, StackProgram}; use synthesizer_snark::UniversalSRS; use indexmap::IndexMap; @@ -2483,3 +2483,94 @@ function {function_name}: assert_ne!(execution_1.peek().unwrap().id(), execution_2.peek().unwrap().id()); assert_ne!(execution_1.to_execution_id().unwrap(), execution_2.to_execution_id().unwrap()); } + +#[test] +fn test_long_import_chain() { + // Initialize a new program. + let program = Program::::from_str( + r" + program test0.aleo; + function c:", + ) + .unwrap(); + + // Construct the process. + let mut process = crate::test_helpers::sample_process(&program); + + // Add 1024 programs to the process. + for i in 1..=1024 { + println!("Adding program {i}"); + // Initialize a new program. + let program = Program::from_str(&format!( + " + import test{}.aleo; + program test{}.aleo; + function c:", + i - 1, + i + )) + .unwrap(); + // Add the program to the process. + process.add_program(&program).unwrap(); + } + + // Add the 1025th program to the process, which should fail. + let program = Program::from_str( + r" + import test1024.aleo; + program test1025. + aleo;function c:", + ) + .unwrap(); + let result = process.add_program(&program); + assert!(result.is_err()); +} + +#[test] +fn test_long_import_chain_with_calls() { + // Initialize a new program. + let program = Program::::from_str( + r" + program test0.aleo; + function c:", + ) + .unwrap(); + + // Construct the process. + let mut process = crate::test_helpers::sample_process(&program); + + // Check that the number of calls is correct. + for i in 1..31 { + println!("Adding program {}", i); + // Initialize a new program. + let program = Program::from_str(&format!( + " + import test{}.aleo; + program test{}.aleo; + function c: + call test{}.aleo/c;", + i - 1, + i, + i - 1 + )) + .unwrap(); + // Add the program to the process. + process.add_program(&program).unwrap(); + // Check that the number of calls is correct. + let stack = process.get_stack(program.id()).unwrap(); + let number_of_calls = stack.get_number_of_calls(program.functions().into_iter().next().unwrap().0).unwrap(); + assert_eq!(number_of_calls, i + 1); + } + + // Check that an additional level of import will fail. + let program = Program::from_str( + r" + import test30.aleo; + program test31.aleo; + function c: + call test30.aleo/c;", + ) + .unwrap(); + let result = process.add_program(&program); + assert!(result.is_err()) +} diff --git a/synthesizer/program/src/traits/stack_and_registers.rs b/synthesizer/program/src/traits/stack_and_registers.rs index 5a5f55f72c..3cbdf7ae5c 100644 --- a/synthesizer/program/src/traits/stack_and_registers.rs +++ b/synthesizer/program/src/traits/stack_and_registers.rs @@ -62,6 +62,9 @@ pub trait StackProgram { /// Returns the program. fn program(&self) -> &Program; + /// Returns the program depth. + fn program_depth(&self) -> usize; + /// Returns the program ID. fn program_id(&self) -> &ProgramID; From 48cfe4ccb67a50e786ad6dfeaad2641cdbc1ac4b Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 12:51:46 -0800 Subject: [PATCH 06/13] Update MAX_IMPORTS --- console/network/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index f8ed3a4339..5667274173 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -160,10 +160,10 @@ pub trait Network: /// The maximum number of outputs per transition. const MAX_OUTPUTS: usize = 16; - /// The maximum number of imports. This is roughly bounded by the `MAX_FUNCTIONS * MAX_TRANSITIONS` - const MAX_IMPORTS: usize = 1024; /// The maximum program depth. const MAX_PROGRAM_DEPTH: usize = 1024; + /// The maximum number of imports. + const MAX_IMPORTS: usize = 2048; /// The state root type. type StateRoot: Bech32ID>; From c342108ba321c263eacef103e1d7fd5287d96760 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:19:53 -0800 Subject: [PATCH 07/13] Fix test --- synthesizer/process/src/tests/test_execute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synthesizer/process/src/tests/test_execute.rs b/synthesizer/process/src/tests/test_execute.rs index 808cf36102..c49aedcc76 100644 --- a/synthesizer/process/src/tests/test_execute.rs +++ b/synthesizer/process/src/tests/test_execute.rs @@ -2518,8 +2518,8 @@ fn test_long_import_chain() { let program = Program::from_str( r" import test1024.aleo; - program test1025. - aleo;function c:", + program test1025.aleo; + function c:", ) .unwrap(); let result = process.add_program(&program); From 38a05d954f46d42060bde89df930d25f15d4f265 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:41:01 -0800 Subject: [PATCH 08/13] Address feedback --- synthesizer/process/src/stack/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 852857fe4e..6e10f4946c 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -224,18 +224,18 @@ impl StackProgram for Stack { &self.program } - /// Returns the program depth. - #[inline] - fn program_depth(&self) -> usize { - self.program_depth - } - /// Returns the program ID. #[inline] fn program_id(&self) -> &ProgramID { self.program.id() } + /// Returns the program depth. + #[inline] + fn program_depth(&self) -> usize { + self.program_depth + } + /// Returns `true` if the stack contains the external record. #[inline] fn contains_external_record(&self, locator: &Locator) -> bool { From 4e7c4f487943adf4722c9606d3b670d6b854e999 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:54:30 -0800 Subject: [PATCH 09/13] Add benches --- synthesizer/process/Cargo.toml | 8 + .../process/benches/stack_operations.rs | 178 ++++++++++++++++++ .../process/src/stack/helpers/initialize.rs | 2 +- .../program/src/traits/stack_and_registers.rs | 6 +- 4 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 synthesizer/process/benches/stack_operations.rs diff --git a/synthesizer/process/Cargo.toml b/synthesizer/process/Cargo.toml index 86336cae5d..294a43b9bd 100644 --- a/synthesizer/process/Cargo.toml +++ b/synthesizer/process/Cargo.toml @@ -45,6 +45,11 @@ wasm = [ ] timer = [ "aleo-std/timer" ] +[[bench]] +name = "stack_operations" +path = "benches/stack_operations.rs" +harness = false + [dependencies.console] package = "snarkvm-console" path = "../../console" @@ -119,6 +124,9 @@ features = [ "preserve_order" ] [dev-dependencies.bincode] version = "1.3" +[dev-dependencies.criterion] +version = "0.5" + [dev-dependencies.ledger-committee] package = "snarkvm-ledger-committee" path = "../../ledger/committee" diff --git a/synthesizer/process/benches/stack_operations.rs b/synthesizer/process/benches/stack_operations.rs new file mode 100644 index 0000000000..f1241d5cfd --- /dev/null +++ b/synthesizer/process/benches/stack_operations.rs @@ -0,0 +1,178 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[macro_use] +extern crate criterion; + +use console::{ + network::MainnetV0, + program::{Identifier, ProgramID}, + types::Field, +}; +use snarkvm_synthesizer_process::{Process, Stack}; +use synthesizer_program::{Program, StackProgram}; + +use circuit::prelude::bail; +use console::{network::Network, prelude::SizeInDataBits}; +use criterion::{BatchSize, Criterion}; +use rand::{distributions::Alphanumeric, Rng}; +use std::str::FromStr; +use utilities::TestRng; + +type CurrentNetwork = MainnetV0; + +fn bench_stack_new(c: &mut Criterion) { + // The depths to benchmark. + const DEPTHS: [usize; 6] = [1, 2, 4, 8, 16, 31]; + + // Initialize an RNG. + let mut rng = TestRng::default(); + + // Initialize a process. + let mut process = Process::load().unwrap(); + + // Benchmark the base case. + c.bench_function("Depth 0 | Stack::new", |b| { + b.iter_batched_ref( + || { + // Sample a random identifier. + let identifier = sample_identifier_as_string::(&mut rng).unwrap(); + // Construct the program. + Program::from_str(&format!("program {identifier}.aleo; function foo:")).unwrap() + }, + |program| Stack::::new(&process, program), + BatchSize::PerIteration, + ) + }); + + // Add the 0th program to the process. + add_program_at_depth(&mut process, 0); + + // Track the depth. + let mut depth = 1; + + for i in DEPTHS { + // Add programs up to the current depth. + while depth < i { + // Add the program to the process. + add_program_at_depth(&mut process, depth); + // Increment the depth. + depth += 1; + } + + // Benchmark at each depth. + c.bench_function(&format!("Depth {i} | Stack::new"), |b| { + b.iter_batched_ref( + || { + // Sample a random identifier. + let identifier = sample_identifier_as_string::(&mut rng).unwrap(); + // Construct the program. + Program::from_str(&format!( + "program {identifier}.aleo; function foo: call test_{i}.aleo/foo;", + identifier = identifier, + i = i - 1 + )) + .unwrap() + }, + |program| Stack::::new(&process, program), + BatchSize::PerIteration, + ) + }); + } +} + +fn bench_stack_get_number_of_calls(c: &mut Criterion) { + // The depths to benchmark. + const DEPTHS: [usize; 6] = [1, 2, 4, 8, 16, 30]; + + // Initialize a process. + let mut process = Process::load().unwrap(); + + // Add the 0th program to the process. + add_program_at_depth(&mut process, 0); + + // Benchmark the `get_number_of_calls` method for the base case. + c.bench_function("Depth 0 | Stack::get_number_of_calls", |b| { + b.iter(|| { + // Get the `Stack` for the 0th program. + let stack = process.get_stack(ProgramID::from_str("test_0.aleo").unwrap()).unwrap(); + // Benchmark the `get_number_of_calls` method. + stack.get_number_of_calls(&Identifier::from_str("foo").unwrap()) + }) + }); + + // Track the depth. + let mut depth = 1; + + for i in DEPTHS { + // Add programs up to the current depth. + while depth <= i { + // Add the program to the process. + add_program_at_depth(&mut process, depth); + // Increment the depth. + depth += 1; + } + + // Get the `Stack` for the current test program. + let stack = process.get_stack(ProgramID::from_str(&format!("test_{}.aleo", i)).unwrap()).unwrap(); + + // Benchmark the `get_number_of_calls` method. + c.bench_function(&format!("Depth {i} | Stack::get_number_of_calls"), |b| { + b.iter(|| stack.get_number_of_calls(&Identifier::from_str("foo").unwrap())) + }); + } +} + +// Adds a program with a given call depth to the process. +fn add_program_at_depth(process: &mut Process, depth: usize) { + // Construct the program. + let program = if depth == 0 { + Program::from_str(r"program test_0.aleo; function foo:").unwrap() + } else { + Program::from_str(&format!( + "import test_{import}.aleo; program test_{current}.aleo; function foo: call test_{import}.aleo/foo;", + import = depth - 1, + current = depth + )) + .unwrap() + }; + + // Add the program to the process. + process.add_program(&program).unwrap(); +} + +// Samples a random identifier as a string. +fn sample_identifier_as_string(rng: &mut TestRng) -> console::prelude::Result { + // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character. + let string = "a".to_string() + + &rng + .sample_iter(&Alphanumeric) + .take(Field::::size_in_data_bits() / (8 * 2)) + .map(char::from) + .collect::(); + // Ensure identifier fits within the data capacity of the base field. + let max_bytes = Field::::size_in_data_bits() / 8; // Note: This intentionally rounds down. + match string.len() <= max_bytes { + // Return the identifier. + true => Ok(string.to_lowercase()), + false => bail!("Identifier exceeds the maximum capacity allowed"), + } +} + +criterion_group! { + name = stack_operations; + config = Criterion::default().sample_size(10); + targets = bench_stack_new, bench_stack_get_number_of_calls +} +criterion_main!(stack_operations); diff --git a/synthesizer/process/src/stack/helpers/initialize.rs b/synthesizer/process/src/stack/helpers/initialize.rs index 7d6d377ba7..98e008ba3b 100644 --- a/synthesizer/process/src/stack/helpers/initialize.rs +++ b/synthesizer/process/src/stack/helpers/initialize.rs @@ -31,7 +31,7 @@ impl Stack { program_depth: 0, }; - // Add all of the imports into the stack. + // Add all the imports into the stack. for import in program.imports().keys() { // Ensure the program imports all exist in the process already. if !process.contains_program(import) { diff --git a/synthesizer/program/src/traits/stack_and_registers.rs b/synthesizer/program/src/traits/stack_and_registers.rs index 3cbdf7ae5c..1b79996aeb 100644 --- a/synthesizer/program/src/traits/stack_and_registers.rs +++ b/synthesizer/program/src/traits/stack_and_registers.rs @@ -62,12 +62,12 @@ pub trait StackProgram { /// Returns the program. fn program(&self) -> &Program; - /// Returns the program depth. - fn program_depth(&self) -> usize; - /// Returns the program ID. fn program_id(&self) -> &ProgramID; + /// Returns the program depth. + fn program_depth(&self) -> usize; + /// Returns `true` if the stack contains the external record. fn contains_external_record(&self, locator: &Locator) -> bool; From 6c8441cb23e90aa1cd29f082aa769e3644bc58aa Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:57:06 -0800 Subject: [PATCH 10/13] Add Cargo.lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 8f828fc23e..5f265ab7ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3375,6 +3375,7 @@ dependencies = [ "aleo-std", "bincode", "colored", + "criterion", "indexmap 2.1.0", "once_cell", "parking_lot", From 9c83b3b990cc04a9032f1079e0ab2a05825db571 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:45:02 -0800 Subject: [PATCH 11/13] Reduce MAX_IMPORTS AND PROGRAM_DEPTH --- console/network/src/lib.rs | 4 ++-- synthesizer/process/src/tests/test_execute.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 5667274173..6598e42849 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -161,9 +161,9 @@ pub trait Network: const MAX_OUTPUTS: usize = 16; /// The maximum program depth. - const MAX_PROGRAM_DEPTH: usize = 1024; + const MAX_PROGRAM_DEPTH: usize = 64; /// The maximum number of imports. - const MAX_IMPORTS: usize = 2048; + const MAX_IMPORTS: usize = 64; /// The state root type. type StateRoot: Bech32ID>; diff --git a/synthesizer/process/src/tests/test_execute.rs b/synthesizer/process/src/tests/test_execute.rs index c49aedcc76..d959068d09 100644 --- a/synthesizer/process/src/tests/test_execute.rs +++ b/synthesizer/process/src/tests/test_execute.rs @@ -2497,8 +2497,8 @@ fn test_long_import_chain() { // Construct the process. let mut process = crate::test_helpers::sample_process(&program); - // Add 1024 programs to the process. - for i in 1..=1024 { + // Add 64 programs to the process. + for i in 1..=64 { println!("Adding program {i}"); // Initialize a new program. let program = Program::from_str(&format!( @@ -2517,8 +2517,8 @@ fn test_long_import_chain() { // Add the 1025th program to the process, which should fail. let program = Program::from_str( r" - import test1024.aleo; - program test1025.aleo; + import test64.aleo; + program test65.aleo; function c:", ) .unwrap(); From 84ca92290e9a1e2ec8795ba1b6934b24364a9aad Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:25:21 -0800 Subject: [PATCH 12/13] Parameterize tests --- synthesizer/process/src/tests/test_execute.rs | 73 ++++++++++++++----- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/synthesizer/process/src/tests/test_execute.rs b/synthesizer/process/src/tests/test_execute.rs index d959068d09..2ba65f7e64 100644 --- a/synthesizer/process/src/tests/test_execute.rs +++ b/synthesizer/process/src/tests/test_execute.rs @@ -25,7 +25,7 @@ use console::{ program::{Identifier, Literal, Plaintext, ProgramID, Record, Value}, types::{Field, U64}, }; -use ledger_block::Fee; +use ledger_block::{Fee, Transaction}; use ledger_query::Query; use ledger_store::{ helpers::memory::{BlockMemory, FinalizeMemory}, @@ -2497,8 +2497,8 @@ fn test_long_import_chain() { // Construct the process. let mut process = crate::test_helpers::sample_process(&program); - // Add 64 programs to the process. - for i in 1..=64 { + // Add `MAX_PROGRAM_DEPTH` programs to the process. + for i in 1..=CurrentNetwork::MAX_PROGRAM_DEPTH { println!("Adding program {i}"); // Initialize a new program. let program = Program::from_str(&format!( @@ -2514,13 +2514,15 @@ fn test_long_import_chain() { process.add_program(&program).unwrap(); } - // Add the 1025th program to the process, which should fail. - let program = Program::from_str( - r" - import test64.aleo; - program test65.aleo; + // Add the `MAX_PROGRAM_DEPTH + 1` program to the process, which should fail. + let program = Program::from_str(&format!( + " + import test{}.aleo; + program test{}.aleo; function c:", - ) + CurrentNetwork::MAX_PROGRAM_DEPTH, + CurrentNetwork::MAX_PROGRAM_DEPTH + 1 + )) .unwrap(); let result = process.add_program(&program); assert!(result.is_err()); @@ -2539,8 +2541,8 @@ fn test_long_import_chain_with_calls() { // Construct the process. let mut process = crate::test_helpers::sample_process(&program); - // Check that the number of calls is correct. - for i in 1..31 { + // Check that the number of calls, up to `Transaction::MAX_TRANSITIONS`, is correct. + for i in 1..Transaction::::MAX_TRANSITIONS { println!("Adding program {}", i); // Initialize a new program. let program = Program::from_str(&format!( @@ -2562,15 +2564,50 @@ fn test_long_import_chain_with_calls() { assert_eq!(number_of_calls, i + 1); } - // Check that an additional level of import will fail. - let program = Program::from_str( - r" - import test30.aleo; - program test31.aleo; + // Check that `Transaction::MAX_TRANSITIONS + 1` calls fails. + let program = Program::from_str(&format!( + " + import test{}.aleo; + program test{}.aleo; function c: - call test30.aleo/c;", - ) + call test{}.aleo/c;", + Transaction::::MAX_TRANSITIONS - 1, + Transaction::::MAX_TRANSITIONS, + Transaction::::MAX_TRANSITIONS - 1 + )) .unwrap(); let result = process.add_program(&program); assert!(result.is_err()) } + +#[test] +fn test_max_imports() { + // Construct the process. + let mut process = Process::::load().unwrap(); + + // Add `MAX_IMPORTS` programs to the process. + for i in 0..CurrentNetwork::MAX_IMPORTS { + println!("Adding program {i}"); + // Initialize a new program. + let program = Program::from_str(&format!("program test{i}.aleo; function c:")).unwrap(); + // Add the program to the process. + process.add_program(&program).unwrap(); + } + + // Add a program importing all `MAX_IMPORTS` programs, which should pass. + let import_string = + (0..CurrentNetwork::MAX_IMPORTS).map(|i| format!("import test{}.aleo;", i)).collect::>().join(" "); + let program = + Program::from_str(&format!("{import_string}program test{}.aleo; function c:", CurrentNetwork::MAX_IMPORTS)) + .unwrap(); + process.add_program(&program).unwrap(); + + // Attempt to construct a program importing `MAX_IMPORTS + 1` programs, which should fail. + let import_string = + (0..CurrentNetwork::MAX_IMPORTS + 1).map(|i| format!("import test{}.aleo;", i)).collect::>().join(" "); + let result = Program::::from_str(&format!( + "{import_string}program test{}.aleo; function c:", + CurrentNetwork::MAX_IMPORTS + 1 + )); + assert!(result.is_err()); +} From 6bbf84d25f0a5d9ebd295cc45089023141e75c03 Mon Sep 17 00:00:00 2001 From: Pranav Gaddamadugu <23022326+d0cd@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:04:04 -0800 Subject: [PATCH 13/13] Fix test --- synthesizer/process/src/tests/test_execute.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/synthesizer/process/src/tests/test_execute.rs b/synthesizer/process/src/tests/test_execute.rs index 2ba65f7e64..8ba8de479b 100644 --- a/synthesizer/process/src/tests/test_execute.rs +++ b/synthesizer/process/src/tests/test_execute.rs @@ -2541,8 +2541,8 @@ fn test_long_import_chain_with_calls() { // Construct the process. let mut process = crate::test_helpers::sample_process(&program); - // Check that the number of calls, up to `Transaction::MAX_TRANSITIONS`, is correct. - for i in 1..Transaction::::MAX_TRANSITIONS { + // Check that the number of calls, up to `Transaction::MAX_TRANSITIONS - 1`, is correct. + for i in 1..(Transaction::::MAX_TRANSITIONS - 1) { println!("Adding program {}", i); // Initialize a new program. let program = Program::from_str(&format!( @@ -2564,16 +2564,16 @@ fn test_long_import_chain_with_calls() { assert_eq!(number_of_calls, i + 1); } - // Check that `Transaction::MAX_TRANSITIONS + 1` calls fails. + // Check that `Transaction::MAX_TRANSITIONS - 1`-th call fails. let program = Program::from_str(&format!( " import test{}.aleo; program test{}.aleo; function c: call test{}.aleo/c;", + Transaction::::MAX_TRANSITIONS - 2, Transaction::::MAX_TRANSITIONS - 1, - Transaction::::MAX_TRANSITIONS, - Transaction::::MAX_TRANSITIONS - 1 + Transaction::::MAX_TRANSITIONS - 2 )) .unwrap(); let result = process.add_program(&program);