From 22c1504a0aeda64bf2ced1236235c54635beceb4 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Wed, 10 Apr 2024 13:13:38 +0200 Subject: [PATCH 1/8] Introduce variable limit for deployed functions, to prevent long constant calculations --- circuit/environment/src/circuit.rs | 43 +++++++++++++++++-- circuit/environment/src/environment.rs | 9 ++++ circuit/network/src/v0.rs | 15 +++++++ console/network/src/lib.rs | 4 +- synthesizer/process/src/stack/deploy.rs | 3 +- synthesizer/process/src/stack/execute.rs | 7 ++- synthesizer/process/src/stack/mod.rs | 5 ++- synthesizer/process/src/tests/test_credits.rs | 2 +- synthesizer/src/vm/mod.rs | 37 ++++++++++++++++ 9 files changed, 114 insertions(+), 11 deletions(-) diff --git a/circuit/environment/src/circuit.rs b/circuit/environment/src/circuit.rs index 56ff721b0c..41ffa23cae 100644 --- a/circuit/environment/src/circuit.rs +++ b/circuit/environment/src/circuit.rs @@ -23,6 +23,7 @@ type Field = ::Field; thread_local! { pub(super) static CONSTRAINT_LIMIT: Cell> = Cell::new(None); + pub(super) static VARIABLE_LIMIT: Cell> = Cell::new(None); pub(super) static CIRCUIT: RefCell> = RefCell::new(R1CS::new()); pub(super) static IN_WITNESS: Cell = Cell::new(false); pub(super) static ZERO: LinearCombination = LinearCombination::zero(); @@ -53,10 +54,20 @@ impl Environment for Circuit { IN_WITNESS.with(|in_witness| { // Ensure we are not in witness mode. if !in_witness.get() { - CIRCUIT.with(|circuit| match mode { - Mode::Constant => circuit.borrow_mut().new_constant(value), - Mode::Public => circuit.borrow_mut().new_public(value), - Mode::Private => circuit.borrow_mut().new_private(value), + CIRCUIT.with(|circuit| { + // Ensure that we do not surpass the variable limit for the circuit. + VARIABLE_LIMIT.with(|variable_limit| { + if let Some(limit) = variable_limit.get() { + if Self::num_variables() > limit { + Self::halt(format!("Surpassed the variable limit ({limit})")) + } + } + }); + match mode { + Mode::Constant => circuit.borrow_mut().new_constant(value), + Mode::Public => circuit.borrow_mut().new_public(value), + Mode::Private => circuit.borrow_mut().new_private(value), + } }) } else { Self::halt("Tried to initialize a new variable in witness mode") @@ -201,6 +212,14 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope()) } + /// Returns the number of variables in the entire circuit. + fn num_variables() -> u64 { + CIRCUIT.with(|circuit| { + let circuit = circuit.borrow(); + circuit.num_constants().saturating_add(circuit.num_public()).saturating_add(circuit.num_private()) + }) + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { CIRCUIT.with(|circuit| circuit.borrow().num_constants()) @@ -268,6 +287,16 @@ impl Environment for Circuit { CONSTRAINT_LIMIT.with(|current_limit| current_limit.replace(limit)); } + /// Returns the variable limit for the circuit, if one exists. + fn get_variable_limit() -> Option { + VARIABLE_LIMIT.with(|current_limit| current_limit.get()) + } + + /// Sets the variable limit for the circuit. + fn set_variable_limit(limit: Option) { + VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit)); + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { CIRCUIT.with(|circuit| { @@ -293,6 +322,8 @@ impl Environment for Circuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); // Ensure the circuit is now empty. @@ -312,6 +343,8 @@ impl Environment for Circuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); assert_eq!(0, circuit.borrow().num_constants()); @@ -330,6 +363,8 @@ impl Environment for Circuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Reset the circuit. *circuit.borrow_mut() = R1CS::<::BaseField>::new(); assert_eq!(0, circuit.borrow().num_constants()); diff --git a/circuit/environment/src/environment.rs b/circuit/environment/src/environment.rs index 541da58919..672c58f9c7 100644 --- a/circuit/environment/src/environment.rs +++ b/circuit/environment/src/environment.rs @@ -110,6 +110,9 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + /// Returns `true` if all constraints in the current scope are satisfied. fn is_satisfied_in_scope() -> bool; + /// Returns the number of variables in the entire environment. + fn num_variables() -> u64; + /// Returns the number of constants in the entire environment. fn num_constants() -> u64; @@ -167,6 +170,12 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + /// Sets the constraint limit for the circuit. fn set_constraint_limit(limit: Option); + /// Returns the variable limit for the circuit, if one exists. + fn get_variable_limit() -> Option; + + /// Sets the variable limit for the circuit. + fn set_variable_limit(limit: Option); + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS); diff --git a/circuit/network/src/v0.rs b/circuit/network/src/v0.rs index 26bda8513c..49e6151122 100644 --- a/circuit/network/src/v0.rs +++ b/circuit/network/src/v0.rs @@ -411,6 +411,11 @@ impl Environment for AleoV0 { E::is_satisfied_in_scope() } + /// Returns the number of variables in the entire circuit. + fn num_variables() -> u64 { + E::num_variables() + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { E::num_constants() @@ -476,6 +481,16 @@ impl Environment for AleoV0 { E::set_constraint_limit(limit) } + /// Returns the variable limit for the circuit, if one exists. + fn get_variable_limit() -> Option { + E::get_variable_limit() + } + + /// Sets the constraint limit for the circuit. + fn set_variable_limit(limit: Option) { + E::set_variable_limit(limit) + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { E::inject_r1cs(r1cs) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 63af2ab15c..9da16c6c6a 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -118,7 +118,9 @@ pub trait Network: /// The cost in microcredits per constraint for the deployment transaction. const SYNTHESIS_FEE_MULTIPLIER: u64 = 25; // 25 microcredits per constraint /// The maximum number of constraints in a deployment. - const MAX_DEPLOYMENT_LIMIT: u64 = 1 << 20; // 1,048,576 constraints + const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 20; // 1,048,576 constraints + /// The maximum number of variables in a deployment. + const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 20; // 1,048,576 variables /// The maximum number of microcredits that can be spent as a fee. const MAX_FEE: u64 = 1_000_000_000_000_000; /// The maximum number of microcredits that can be spent on a finalize block. diff --git a/synthesizer/process/src/stack/deploy.rs b/synthesizer/process/src/stack/deploy.rs index be662d134f..7ed697f879 100644 --- a/synthesizer/process/src/stack/deploy.rs +++ b/synthesizer/process/src/stack/deploy.rs @@ -74,7 +74,7 @@ impl Stack { let program_id = self.program.id(); // Check that the number of combined constraints does not exceed the deployment limit. - ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_LIMIT); + ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS); // Construct the call stacks and assignments used to verify the certificates. let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len()); @@ -143,6 +143,7 @@ impl Stack { burner_private_key, assignments.clone(), Some(constraint_limit as u64), + Some(N::MAX_DEPLOYMENT_VARIABLES), ); // Append the function name, callstack, and assignments. call_stacks.push((function.name(), call_stack, assignments)); diff --git a/synthesizer/process/src/stack/execute.rs b/synthesizer/process/src/stack/execute.rs index 0039eb4f03..bcf8f023c4 100644 --- a/synthesizer/process/src/stack/execute.rs +++ b/synthesizer/process/src/stack/execute.rs @@ -149,8 +149,9 @@ impl StackExecute for Stack { // If in 'CheckDeployment' mode, set the constraint limit. // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those. - if let CallStack::CheckDeployment(_, _, _, constraint_limit) = &call_stack { + if let CallStack::CheckDeployment(_, _, _, constraint_limit, var_limit) = &call_stack { A::set_constraint_limit(*constraint_limit); + A::set_variable_limit(*var_limit); } // Retrieve the next request. @@ -410,6 +411,8 @@ impl StackExecute for Stack { self.matches_value_type(output, output_type) })?; + println!("A::num_constants(): {}", A::num_constants()); + // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied. if matches!(registers.call_stack(), CallStack::Execute(..) | CallStack::PackageRun(..)) { // If the circuit is empty or not satisfied, then throw an error. @@ -445,7 +448,7 @@ impl StackExecute for Stack { lap!(timer, "Save the transition"); } // If the circuit is in `CheckDeployment` mode, then save the assignment. - else if let CallStack::CheckDeployment(_, _, ref assignments, _) = registers.call_stack() { + else if let CallStack::CheckDeployment(_, _, ref assignments, _, _) = registers.call_stack() { // Construct the call metrics. let metrics = CallMetrics { program_id: *self.program_id(), diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index aa0f7af67b..070b1391dd 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -81,7 +81,7 @@ pub type Assignments = Arc pub enum CallStack { Authorize(Vec>, PrivateKey, Authorization), Synthesize(Vec>, PrivateKey, Authorization), - CheckDeployment(Vec>, PrivateKey, Assignments, Option), + CheckDeployment(Vec>, PrivateKey, Assignments, Option, Option), Evaluate(Authorization), Execute(Authorization, Arc>>), PackageRun(Vec>, PrivateKey, Assignments), @@ -109,12 +109,13 @@ impl CallStack { CallStack::Synthesize(requests, private_key, authorization) => { CallStack::Synthesize(requests.clone(), *private_key, authorization.replicate()) } - CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit) => { + CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit, variable_limit) => { CallStack::CheckDeployment( requests.clone(), *private_key, Arc::new(RwLock::new(assignments.read().clone())), *constraint_limit, + *variable_limit, ) } CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()), diff --git a/synthesizer/process/src/tests/test_credits.rs b/synthesizer/process/src/tests/test_credits.rs index 0986c204f9..003d6068b8 100644 --- a/synthesizer/process/src/tests/test_credits.rs +++ b/synthesizer/process/src/tests/test_credits.rs @@ -2249,7 +2249,7 @@ mod sanity_checks { // Initialize the assignments. let assignments = Assignments::::default(); // Initialize the call stack. - let call_stack = CallStack::CheckDeployment(vec![request], *private_key, assignments.clone(), None); + let call_stack = CallStack::CheckDeployment(vec![request], *private_key, assignments.clone(), None, None); // Synthesize the circuit. let _response = stack.execute_function::(call_stack, None, None, rng).unwrap(); // Retrieve the assignment. diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 5c78a84fb6..d910ebe360 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -1320,6 +1320,43 @@ function do: assert!(vm.check_transaction(&deployment, None, rng).is_err()); } + #[test] + fn test_deployment_num_constant_overload() { + let rng = &mut TestRng::default(); + + // Initialize a private key. + let private_key = sample_genesis_private_key(rng); + + // Initialize the genesis block. + let genesis = sample_genesis_block(rng); + + // Initialize the VM. + let vm = sample_vm(); + // Update the VM. + vm.add_next_block(&genesis).unwrap(); + + // Deploy the base program. + let program = Program::from_str( + r" +program synthesis_num_constants.aleo; + +function do: + cast 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 into r0 as [u32; 32u32]; + cast r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 into r1 as [[u32; 32u32]; 32u32]; + cast r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 into r2 as [[[u32; 32u32]; 32u32]; 16u32]; + hash.bhp1024 r2 into r3 as u32; + output r3 as u32.private;", + ) + .unwrap(); + + // Create the deployment transaction. + let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); + + // Verify the deployment transaction. It should fail because there are too many constants. + let check_tx_res = vm.check_transaction(&deployment, None, rng); + assert!(check_tx_res.is_err()); + } + #[test] fn test_deployment_synthesis_overreport() { let rng = &mut TestRng::default(); From 15fabe76ea205090996348b60ef968809b7c1aa4 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Wed, 10 Apr 2024 20:58:51 +0200 Subject: [PATCH 2/8] Ensure MAX_DEPLOYMENT_VARIABLES limits the total number of variables in a program --- console/network/src/lib.rs | 2 +- synthesizer/process/src/stack/deploy.rs | 4 +++- synthesizer/process/src/stack/execute.rs | 6 ++++++ synthesizer/process/src/trace/call_metrics/mod.rs | 1 + synthesizer/src/vm/mod.rs | 9 ++++++++- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 9da16c6c6a..8d052a60ea 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -120,7 +120,7 @@ pub trait Network: /// The maximum number of constraints in a deployment. const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 20; // 1,048,576 constraints /// The maximum number of variables in a deployment. - const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 20; // 1,048,576 variables + const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 22; // 4,194,304 variables /// The maximum number of microcredits that can be spent as a fee. const MAX_FEE: u64 = 1_000_000_000_000_000; /// The maximum number of microcredits that can be spent on a finalize block. diff --git a/synthesizer/process/src/stack/deploy.rs b/synthesizer/process/src/stack/deploy.rs index 7ed697f879..3836a799d7 100644 --- a/synthesizer/process/src/stack/deploy.rs +++ b/synthesizer/process/src/stack/deploy.rs @@ -132,6 +132,8 @@ impl Stack { lap!(timer, "Compute the request for {}", function.name()); // Initialize the assignments. let assignments = Assignments::::default(); + // Initialize the variable limit. + let variable_limit = N::MAX_DEPLOYMENT_VARIABLES / N::MAX_FUNCTIONS as u64; // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding. let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else { // Since a deployment must always pay non-zero fee, it must always have at least one constraint. @@ -143,7 +145,7 @@ impl Stack { burner_private_key, assignments.clone(), Some(constraint_limit as u64), - Some(N::MAX_DEPLOYMENT_VARIABLES), + Some(variable_limit), ); // Append the function name, callstack, and assignments. call_stacks.push((function.name(), call_stack, assignments)); diff --git a/synthesizer/process/src/stack/execute.rs b/synthesizer/process/src/stack/execute.rs index bcf8f023c4..9dca27e6b4 100644 --- a/synthesizer/process/src/stack/execute.rs +++ b/synthesizer/process/src/stack/execute.rs @@ -425,6 +425,9 @@ impl StackExecute for Stack { ); } + // Determine the number of variables allocated in the circuit. + let num_variables = A::num_variables(); + // Eject the circuit assignment and reset the circuit. let assignment = A::eject_assignment_and_reset(); @@ -453,6 +456,7 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), + num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, @@ -475,6 +479,7 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), + num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, @@ -495,6 +500,7 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), + num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, diff --git a/synthesizer/process/src/trace/call_metrics/mod.rs b/synthesizer/process/src/trace/call_metrics/mod.rs index d45fec6d94..4a68280811 100644 --- a/synthesizer/process/src/trace/call_metrics/mod.rs +++ b/synthesizer/process/src/trace/call_metrics/mod.rs @@ -22,6 +22,7 @@ pub struct CallMetrics { pub program_id: ProgramID, pub function_name: Identifier, pub num_instructions: usize, + pub num_variables: u64, pub num_request_constraints: u64, pub num_function_constraints: u64, pub num_response_constraints: u64, diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index d910ebe360..c516ec8550 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -1343,7 +1343,14 @@ program synthesis_num_constants.aleo; function do: cast 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 into r0 as [u32; 32u32]; cast r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 into r1 as [[u32; 32u32]; 32u32]; - cast r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 r1 into r2 as [[[u32; 32u32]; 32u32]; 16u32]; + cast r1 r1 r1 r1 r1 into r2 as [[[u32; 32u32]; 32u32]; 5u32]; + hash.bhp1024 r2 into r3 as u32; + output r3 as u32.private; + +function do2: + cast 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 0u32 into r0 as [u32; 32u32]; + cast r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 r0 into r1 as [[u32; 32u32]; 32u32]; + cast r1 r1 r1 r1 r1 into r2 as [[[u32; 32u32]; 32u32]; 5u32]; hash.bhp1024 r2 into r3 as u32; output r3 as u32.private;", ) From 88b5ffb22fe8098f699f1f8fb11f062ed796564a Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Thu, 11 Apr 2024 13:07:52 +0200 Subject: [PATCH 3/8] Track num_variables for each function in a Deployment to prevent going over those limits Lower MAX_DEPLOYMENT_VARIABLES Initialize circuit gadget constants before synthesis Update test expectation --- circuit/environment/src/circuit.rs | 22 +++-- circuit/environment/src/environment.rs | 6 +- circuit/environment/src/helpers/counter.rs | 9 ++ circuit/environment/src/helpers/r1cs.rs | 5 + circuit/network/src/lib.rs | 3 + circuit/network/src/v0.rs | 33 ++++++- console/network/src/lib.rs | 2 +- .../block/src/transaction/deployment/bytes.rs | 16 ++-- .../block/src/transaction/deployment/mod.rs | 49 +++++++--- .../src/transaction/deployment/serialize.rs | 4 +- .../store/src/helpers/memory/transaction.rs | 9 ++ .../store/src/helpers/rocksdb/internal/id.rs | 2 + .../store/src/helpers/rocksdb/transaction.rs | 9 ++ ledger/store/src/transaction/deployment.rs | 30 ++++-- synthesizer/process/src/deploy.rs | 2 +- synthesizer/process/src/finalize.rs | 2 +- synthesizer/process/src/stack/deploy.rs | 16 ++-- synthesizer/process/src/stack/execute.rs | 16 ++-- .../process/src/stack/helpers/initialize.rs | 1 + .../process/src/stack/helpers/synthesize.rs | 7 +- synthesizer/process/src/stack/mod.rs | 32 +++++++ .../process/src/trace/call_metrics/mod.rs | 1 - synthesizer/src/vm/mod.rs | 91 +++++++++++++++++-- .../vm/execute_and_finalize/test_rand.out | 2 +- 24 files changed, 292 insertions(+), 77 deletions(-) diff --git a/circuit/environment/src/circuit.rs b/circuit/environment/src/circuit.rs index 41ffa23cae..f524a2d77e 100644 --- a/circuit/environment/src/circuit.rs +++ b/circuit/environment/src/circuit.rs @@ -58,7 +58,9 @@ impl Environment for Circuit { // Ensure that we do not surpass the variable limit for the circuit. VARIABLE_LIMIT.with(|variable_limit| { if let Some(limit) = variable_limit.get() { - if Self::num_variables() > limit { + // NOTE: we can use this function because circuits only have a single scope. + // Once we have nested scopes, we will need to track the number of variables in each scope. + if Self::num_variables_in_scope() > limit { Self::halt(format!("Surpassed the variable limit ({limit})")) } } @@ -212,14 +214,6 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope()) } - /// Returns the number of variables in the entire circuit. - fn num_variables() -> u64 { - CIRCUIT.with(|circuit| { - let circuit = circuit.borrow(); - circuit.num_constants().saturating_add(circuit.num_public()).saturating_add(circuit.num_private()) - }) - } - /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { CIRCUIT.with(|circuit| circuit.borrow().num_constants()) @@ -245,6 +239,11 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros()) } + /// Returns the number of variables in the entire circuit. + fn num_variables_in_scope() -> u64 { + CIRCUIT.with(|circuit| circuit.borrow().num_variables_in_scope()) + } + /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope()) @@ -304,6 +303,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -311,6 +311,7 @@ impl Environment for Circuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); + assert_eq!(0, r1cs.num_variables_in_scope()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -330,6 +331,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -350,6 +352,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -370,6 +373,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); }); } diff --git a/circuit/environment/src/environment.rs b/circuit/environment/src/environment.rs index 672c58f9c7..c0d2cd87fb 100644 --- a/circuit/environment/src/environment.rs +++ b/circuit/environment/src/environment.rs @@ -110,9 +110,6 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + /// Returns `true` if all constraints in the current scope are satisfied. fn is_satisfied_in_scope() -> bool; - /// Returns the number of variables in the entire environment. - fn num_variables() -> u64; - /// Returns the number of constants in the entire environment. fn num_constants() -> u64; @@ -133,6 +130,9 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + (Self::num_constants(), Self::num_public(), Self::num_private(), Self::num_constraints(), Self::num_nonzeros()) } + /// Returns the number of variables for the current scope. + fn num_variables_in_scope() -> u64; + /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64; diff --git a/circuit/environment/src/helpers/counter.rs b/circuit/environment/src/helpers/counter.rs index 8631f95af1..8f3559a20d 100644 --- a/circuit/environment/src/helpers/counter.rs +++ b/circuit/environment/src/helpers/counter.rs @@ -24,6 +24,7 @@ pub(crate) struct Counter { constants: u64, public: u64, private: u64, + total_variables: u64, nonzeros: (u64, u64, u64), parents: Vec<(Scope, Vec>>, u64, u64, u64, (u64, u64, u64))>, } @@ -114,16 +115,24 @@ impl Counter { /// Increments the number of constants by 1. pub(crate) fn increment_constant(&mut self) { self.constants += 1; + self.total_variables += 1; } /// Increments the number of public variables by 1. pub(crate) fn increment_public(&mut self) { self.public += 1; + self.total_variables += 1; } /// Increments the number of private variables by 1. pub(crate) fn increment_private(&mut self) { self.private += 1; + self.total_variables += 1; + } + + /// Returns the number of variables in scope. + pub(crate) fn num_variables_in_scope(&self) -> u64 { + self.total_variables } /// Returns the number of constants in scope in scope. diff --git a/circuit/environment/src/helpers/r1cs.rs b/circuit/environment/src/helpers/r1cs.rs index d08adbdfdb..59b6a69701 100644 --- a/circuit/environment/src/helpers/r1cs.rs +++ b/circuit/environment/src/helpers/r1cs.rs @@ -159,6 +159,11 @@ impl R1CS { self.nonzeros } + /// Returns the total number of variables for the current scope. + pub fn num_variables_in_scope(&self) -> u64 { + self.counter.num_variables_in_scope() + } + /// Returns the number of constants for the current scope. pub(crate) fn num_constants_in_scope(&self) -> u64 { self.counter.num_constants_in_scope() diff --git a/circuit/network/src/lib.rs b/circuit/network/src/lib.rs index dd6ac0a7fa..acabf0891a 100644 --- a/circuit/network/src/lib.rs +++ b/circuit/network/src/lib.rs @@ -29,6 +29,9 @@ pub trait Aleo: Environment { /// The maximum number of field elements in data (must not exceed u16::MAX). const MAX_DATA_SIZE_IN_FIELDS: u32 = ::MAX_DATA_SIZE_IN_FIELDS; + /// Initializes all of the constants for the Aleo environment. + fn init_constants(); + /// Returns the encryption domain as a constant field element. fn encryption_domain() -> Field; diff --git a/circuit/network/src/v0.rs b/circuit/network/src/v0.rs index 49e6151122..8cd66f3e74 100644 --- a/circuit/network/src/v0.rs +++ b/circuit/network/src/v0.rs @@ -101,6 +101,29 @@ thread_local! { pub struct AleoV0; impl Aleo for AleoV0 { + /// Initializes all of the constants for the Aleo environment. + fn init_constants() { + GENERATOR_G.with(|_| ()); + ENCRYPTION_DOMAIN.with(|_| ()); + GRAPH_KEY_DOMAIN.with(|_| ()); + SERIAL_NUMBER_DOMAIN.with(|_| ()); + BHP_256.with(|_| ()); + BHP_512.with(|_| ()); + BHP_768.with(|_| ()); + BHP_1024.with(|_| ()); + KECCAK_256.with(|_| ()); + KECCAK_384.with(|_| ()); + KECCAK_512.with(|_| ()); + PEDERSEN_64.with(|_| ()); + PEDERSEN_128.with(|_| ()); + POSEIDON_2.with(|_| ()); + POSEIDON_4.with(|_| ()); + POSEIDON_8.with(|_| ()); + SHA3_256.with(|_| ()); + SHA3_384.with(|_| ()); + SHA3_512.with(|_| ()); + } + /// Returns the encryption domain as a constant field element. fn encryption_domain() -> Field { ENCRYPTION_DOMAIN.with(|domain| domain.clone()) @@ -411,11 +434,6 @@ impl Environment for AleoV0 { E::is_satisfied_in_scope() } - /// Returns the number of variables in the entire circuit. - fn num_variables() -> u64 { - E::num_variables() - } - /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { E::num_constants() @@ -441,6 +459,11 @@ impl Environment for AleoV0 { E::num_nonzeros() } + /// Returns the number of variables for the current scope. + fn num_variables_in_scope() -> u64 { + E::num_variables_in_scope() + } + /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { E::num_constants_in_scope() diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 8d052a60ea..9da16c6c6a 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -120,7 +120,7 @@ pub trait Network: /// The maximum number of constraints in a deployment. const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 20; // 1,048,576 constraints /// The maximum number of variables in a deployment. - const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 22; // 4,194,304 variables + const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 20; // 1,048,576 variables /// The maximum number of microcredits that can be spent as a fee. const MAX_FEE: u64 = 1_000_000_000_000_000; /// The maximum number of microcredits that can be spent on a finalize block. diff --git a/ledger/block/src/transaction/deployment/bytes.rs b/ledger/block/src/transaction/deployment/bytes.rs index e8e97757a6..a4aed9266d 100644 --- a/ledger/block/src/transaction/deployment/bytes.rs +++ b/ledger/block/src/transaction/deployment/bytes.rs @@ -31,8 +31,8 @@ impl FromBytes for Deployment { // Read the number of entries in the bundle. let num_entries = u16::read_le(&mut reader)?; - // Read the verifying keys. - let mut verifying_keys = Vec::with_capacity(num_entries as usize); + // Read the function specifications. + let mut function_specs = Vec::with_capacity(num_entries as usize); for _ in 0..num_entries { // Read the identifier. let identifier = Identifier::::read_le(&mut reader)?; @@ -40,12 +40,14 @@ impl FromBytes for Deployment { let verifying_key = VerifyingKey::::read_le(&mut reader)?; // Read the certificate. let certificate = Certificate::::read_le(&mut reader)?; + // Read the variable count. + let variable_count = u64::read_le(&mut reader)?; // Add the entry. - verifying_keys.push((identifier, (verifying_key, certificate))); + function_specs.push((identifier, (verifying_key, certificate, variable_count))); } // Return the deployment. - Self::new(edition, program, verifying_keys).map_err(|err| error(format!("{err}"))) + Self::new(edition, program, function_specs).map_err(|err| error(format!("{err}"))) } } @@ -59,15 +61,17 @@ impl ToBytes for Deployment { // Write the program. self.program.write_le(&mut writer)?; // Write the number of entries in the bundle. - (u16::try_from(self.verifying_keys.len()).map_err(|e| error(e.to_string()))?).write_le(&mut writer)?; + (u16::try_from(self.function_specs.len()).map_err(|e| error(e.to_string()))?).write_le(&mut writer)?; // Write each entry. - for (function_name, (verifying_key, certificate)) in &self.verifying_keys { + for (function_name, (verifying_key, certificate, variable_count)) in &self.function_specs { // Write the function name. function_name.write_le(&mut writer)?; // Write the verifying key. verifying_key.write_le(&mut writer)?; // Write the certificate. certificate.write_le(&mut writer)?; + // Write the variable count. + variable_count.write_le(&mut writer)?; } Ok(()) } diff --git a/ledger/block/src/transaction/deployment/mod.rs b/ledger/block/src/transaction/deployment/mod.rs index bf898b41ba..105f5b12ee 100644 --- a/ledger/block/src/transaction/deployment/mod.rs +++ b/ledger/block/src/transaction/deployment/mod.rs @@ -27,25 +27,27 @@ use console::{ use synthesizer_program::Program; use synthesizer_snark::{Certificate, VerifyingKey}; +/// The verification key, certificate, and number of variables for a function. +type FunctionSpec = (VerifyingKey, Certificate, u64); + #[derive(Clone, PartialEq, Eq)] pub struct Deployment { /// The edition. edition: u16, /// The program. program: Program, - /// The mapping of function names to their verifying key and certificate. - verifying_keys: Vec<(Identifier, (VerifyingKey, Certificate))>, + /// The mapping of function names to their specification. + function_specs: Vec<(Identifier, FunctionSpec)>, } - impl Deployment { /// Initializes a new deployment. pub fn new( edition: u16, program: Program, - verifying_keys: Vec<(Identifier, (VerifyingKey, Certificate))>, + function_specs: Vec<(Identifier, FunctionSpec)>, ) -> Result { // Construct the deployment. - let deployment = Self { edition, program, verifying_keys }; + let deployment = Self { edition, program, function_specs }; // Ensure the deployment is ordered. deployment.check_is_ordered()?; // Return the deployment. @@ -70,17 +72,17 @@ impl Deployment { ); // Ensure the deployment contains verifying keys. ensure!( - !self.verifying_keys.is_empty(), + !self.function_specs.is_empty(), "No verifying keys present in the deployment for program '{program_id}'" ); - // Ensure the number of functions matches the number of verifying keys. - if self.program.functions().len() != self.verifying_keys.len() { + // Ensure the number of functions matches the number of function specifications. + if self.program.functions().len() != self.function_specs.len() { bail!("Deployment has an incorrect number of verifying keys, according to the program."); } - // Ensure the function and verifying keys correspond. - for ((function_name, function), (name, _)) in self.program.functions().iter().zip_eq(&self.verifying_keys) { + // Ensure the function and specs correspond. + for ((function_name, function), (name, _)) in self.program.functions().iter().zip_eq(&self.function_specs) { // Ensure the function name is correct. if function_name != function.name() { bail!("The function key is '{function_name}', but the function name is '{}'", function.name()) @@ -92,7 +94,7 @@ impl Deployment { } ensure!( - !has_duplicates(self.verifying_keys.iter().map(|(name, ..)| name)), + !has_duplicates(self.function_specs.iter().map(|(name, ..)| name)), "A duplicate function name was found" ); @@ -119,9 +121,9 @@ impl Deployment { self.program.id() } - /// Returns the verifying keys. - pub const fn verifying_keys(&self) -> &Vec<(Identifier, (VerifyingKey, Certificate))> { - &self.verifying_keys + /// Returns the function specifications. + pub const fn verifying_keys(&self) -> &Vec<(Identifier, FunctionSpec)> { + &self.function_specs } /// Returns the sum of the constraint counts for all functions in this deployment. @@ -129,7 +131,7 @@ impl Deployment { // Initialize the accumulator. let mut num_combined_constraints = 0u64; // Iterate over the functions. - for (_, (vk, _)) in &self.verifying_keys { + for (_, (vk, _, _)) in &self.function_specs { // Add the number of constraints. // Note: This method must be *checked* because the claimed constraint count // is from the user, not the synthesizer. @@ -141,6 +143,23 @@ impl Deployment { Ok(num_combined_constraints) } + /// Returns the sum of the variable counts for all functions in this deployment. + pub fn num_combined_variables(&self) -> Result { + // Initialize the accumulator. + let mut num_combined_variables = 0u64; + // Iterate over the functions. + for (_, (_, _, variable_count)) in &self.function_specs { + // Add the number of variables. + // Note: This method must be *checked* because the claimed variable count + // is from the user, not the synthesizer. + num_combined_variables = num_combined_variables + .checked_add(*variable_count) + .ok_or_else(|| anyhow!("Overflow when counting variables for '{}'", self.program_id()))?; + } + // Return the number of combined constraints. + Ok(num_combined_variables) + } + /// Returns the deployment ID. pub fn to_deployment_id(&self) -> Result> { Ok(*Transaction::deployment_tree(self, None)?.root()) diff --git a/ledger/block/src/transaction/deployment/serialize.rs b/ledger/block/src/transaction/deployment/serialize.rs index 88af802fc9..ee9ac51cd5 100644 --- a/ledger/block/src/transaction/deployment/serialize.rs +++ b/ledger/block/src/transaction/deployment/serialize.rs @@ -22,7 +22,7 @@ impl Serialize for Deployment { let mut deployment = serializer.serialize_struct("Deployment", 3)?; deployment.serialize_field("edition", &self.edition)?; deployment.serialize_field("program", &self.program)?; - deployment.serialize_field("verifying_keys", &self.verifying_keys)?; + deployment.serialize_field("function_specs", &self.function_specs)?; deployment.end() } false => ToBytesSerializer::serialize_with_size_encoding(self, serializer), @@ -45,7 +45,7 @@ impl<'de, N: Network> Deserialize<'de> for Deployment { // Retrieve the program. DeserializeExt::take_from_value::(&mut deployment, "program")?, // Retrieve the verifying keys. - DeserializeExt::take_from_value::(&mut deployment, "verifying_keys")?, + DeserializeExt::take_from_value::(&mut deployment, "function_specs")?, ) .map_err(de::Error::custom)?; diff --git a/ledger/store/src/helpers/memory/transaction.rs b/ledger/store/src/helpers/memory/transaction.rs index 66b7b14867..eb80a51af4 100644 --- a/ledger/store/src/helpers/memory/transaction.rs +++ b/ledger/store/src/helpers/memory/transaction.rs @@ -103,6 +103,8 @@ pub struct DeploymentMemory { verifying_key_map: MemoryMap<(ProgramID, Identifier, u16), VerifyingKey>, /// The certificate map. certificate_map: MemoryMap<(ProgramID, Identifier, u16), Certificate>, + /// The variable count map. + variable_count_map: MemoryMap<(ProgramID, Identifier, u16), u64>, /// The fee store. fee_store: FeeStore>, } @@ -116,6 +118,7 @@ impl DeploymentStorage for DeploymentMemory { type ProgramMap = MemoryMap<(ProgramID, u16), Program>; type VerifyingKeyMap = MemoryMap<(ProgramID, Identifier, u16), VerifyingKey>; type CertificateMap = MemoryMap<(ProgramID, Identifier, u16), Certificate>; + type VariableCountMap = MemoryMap<(ProgramID, Identifier, u16), u64>; type FeeStorage = FeeMemory; /// Initializes the deployment storage. @@ -128,6 +131,7 @@ impl DeploymentStorage for DeploymentMemory { program_map: MemoryMap::default(), verifying_key_map: MemoryMap::default(), certificate_map: MemoryMap::default(), + variable_count_map: MemoryMap::default(), fee_store, }) } @@ -167,6 +171,11 @@ impl DeploymentStorage for DeploymentMemory { &self.certificate_map } + /// Returns the variable count map. + fn variable_count_map(&self) -> &Self::VariableCountMap { + &self.variable_count_map + } + /// Returns the fee store. fn fee_store(&self) -> &FeeStore { &self.fee_store diff --git a/ledger/store/src/helpers/rocksdb/internal/id.rs b/ledger/store/src/helpers/rocksdb/internal/id.rs index 6093fdad74..4ada48b0c1 100644 --- a/ledger/store/src/helpers/rocksdb/internal/id.rs +++ b/ledger/store/src/helpers/rocksdb/internal/id.rs @@ -110,6 +110,7 @@ pub enum DeploymentMap { Program = DataID::DeploymentProgramMap as u16, VerifyingKey = DataID::DeploymentVerifyingKeyMap as u16, Certificate = DataID::DeploymentCertificateMap as u16, + VariableCount = DataID::DeploymentVariableCountMap as u16, } /// The RocksDB map prefix for execution-related entries. @@ -252,6 +253,7 @@ enum DataID { DeploymentProgramMap, DeploymentVerifyingKeyMap, DeploymentCertificateMap, + DeploymentVariableCountMap, // Execution ExecutionIDMap, ExecutionReverseIDMap, diff --git a/ledger/store/src/helpers/rocksdb/transaction.rs b/ledger/store/src/helpers/rocksdb/transaction.rs index 2cac1c9c2c..ff1610d77e 100644 --- a/ledger/store/src/helpers/rocksdb/transaction.rs +++ b/ledger/store/src/helpers/rocksdb/transaction.rs @@ -113,6 +113,8 @@ pub struct DeploymentDB { verifying_key_map: DataMap<(ProgramID, Identifier, u16), VerifyingKey>, /// The certificate map. certificate_map: DataMap<(ProgramID, Identifier, u16), Certificate>, + /// The variable count map. + variable_count_map: DataMap<(ProgramID, Identifier, u16), u64>, /// The fee store. fee_store: FeeStore>, } @@ -126,6 +128,7 @@ impl DeploymentStorage for DeploymentDB { type ProgramMap = DataMap<(ProgramID, u16), Program>; type VerifyingKeyMap = DataMap<(ProgramID, Identifier, u16), VerifyingKey>; type CertificateMap = DataMap<(ProgramID, Identifier, u16), Certificate>; + type VariableCountMap = DataMap<(ProgramID, Identifier, u16), u64>; type FeeStorage = FeeDB; /// Initializes the deployment storage. @@ -140,6 +143,7 @@ impl DeploymentStorage for DeploymentDB { program_map: rocksdb::RocksDB::open_map(N::ID, storage_mode.clone(), MapID::Deployment(DeploymentMap::Program))?, verifying_key_map: rocksdb::RocksDB::open_map(N::ID, storage_mode.clone(), MapID::Deployment(DeploymentMap::VerifyingKey))?, certificate_map: rocksdb::RocksDB::open_map(N::ID, storage_mode.clone(), MapID::Deployment(DeploymentMap::Certificate))?, + variable_count_map: rocksdb::RocksDB::open_map(N::ID, storage_mode.clone(), MapID::Deployment(DeploymentMap::VariableCount))?, fee_store, }) } @@ -179,6 +183,11 @@ impl DeploymentStorage for DeploymentDB { &self.certificate_map } + /// Returns the variable count map. + fn variable_count_map(&self) -> &Self::VariableCountMap { + &self.variable_count_map + } + /// Returns the fee store. fn fee_store(&self) -> &FeeStore { &self.fee_store diff --git a/ledger/store/src/transaction/deployment.rs b/ledger/store/src/transaction/deployment.rs index bdc7d95b46..1b41181c41 100644 --- a/ledger/store/src/transaction/deployment.rs +++ b/ledger/store/src/transaction/deployment.rs @@ -49,6 +49,8 @@ pub trait DeploymentStorage: Clone + Send + Sync { type VerifyingKeyMap: for<'a> Map<'a, (ProgramID, Identifier, u16), VerifyingKey>; /// The mapping of `(program ID, function name, edition)` to `certificate`. type CertificateMap: for<'a> Map<'a, (ProgramID, Identifier, u16), Certificate>; + /// The mapping of `(program ID, function name, edition)` to `variable count`. + type VariableCountMap: for<'a> Map<'a, (ProgramID, Identifier, u16), u64>; /// The fee storage. type FeeStorage: FeeStorage; @@ -69,6 +71,8 @@ pub trait DeploymentStorage: Clone + Send + Sync { fn verifying_key_map(&self) -> &Self::VerifyingKeyMap; /// Returns the certificate map. fn certificate_map(&self) -> &Self::CertificateMap; + /// Returns the variable count map. + fn variable_count_map(&self) -> &Self::VariableCountMap; /// Returns the fee storage. fn fee_store(&self) -> &FeeStore; @@ -196,11 +200,13 @@ pub trait DeploymentStorage: Clone + Send + Sync { self.program_map().insert((program_id, edition), program.clone())?; // Store the verifying keys and certificates. - for (function_name, (verifying_key, certificate)) in deployment.verifying_keys() { + for (function_name, (verifying_key, certificate, variable_count)) in deployment.verifying_keys() { // Store the verifying key. self.verifying_key_map().insert((program_id, *function_name, edition), verifying_key.clone())?; // Store the certificate. self.certificate_map().insert((program_id, *function_name, edition), certificate.clone())?; + // Store the variable count. + self.variable_count_map().insert((program_id, *function_name, edition), *variable_count)?; } // Store the fee transition. @@ -400,27 +406,37 @@ pub trait DeploymentStorage: Clone + Send + Sync { None => bail!("Failed to get the deployed program '{program_id}' (edition {edition})"), }; - // Initialize a vector for the verifying keys and certificates. - let mut verifying_keys = Vec::with_capacity(program.functions().len()); + // Initialize a vector for the function_specs. + let mut function_specs = Vec::with_capacity(program.functions().len()); // Retrieve the verifying keys and certificates. for function_name in program.functions().keys() { // Retrieve the verifying key. let verifying_key = match self.verifying_key_map().get_confirmed(&(program_id, *function_name, edition))? { Some(verifying_key) => cow_to_cloned!(verifying_key), - None => bail!("Failed to get the verifying key for '{program_id}/{function_name}' (edition {edition})"), + None => { + bail!("Failed to get the verifying key for '{program_id}/{function_name}' (edition {edition})") + } }; // Retrieve the certificate. let certificate = match self.certificate_map().get_confirmed(&(program_id, *function_name, edition))? { Some(certificate) => cow_to_cloned!(certificate), None => bail!("Failed to get the certificate for '{program_id}/{function_name}' (edition {edition})"), }; - // Add the verifying key and certificate to the deployment. - verifying_keys.push((*function_name, (verifying_key, certificate))); + // Retrieve the variable count. + let variable_count = + match self.variable_count_map().get_confirmed(&(program_id, *function_name, edition))? { + Some(variable_count) => cow_to_cloned!(variable_count), + None => { + bail!("Failed to get the variable count for '{program_id}/{function_name}' (edition {edition})") + } + }; + // Add the verifying key, certificate and variable_count to the deployment. + function_specs.push((*function_name, (verifying_key, certificate, variable_count))); } // Return the deployment. - Ok(Some(Deployment::new(edition, program, verifying_keys)?)) + Ok(Some(Deployment::new(edition, program, function_specs)?)) } /// Returns the fee for the given `transaction ID`. diff --git a/synthesizer/process/src/deploy.rs b/synthesizer/process/src/deploy.rs index b876a37f0e..5bd6a23487 100644 --- a/synthesizer/process/src/deploy.rs +++ b/synthesizer/process/src/deploy.rs @@ -48,7 +48,7 @@ impl Process { lap!(timer, "Compute the stack"); // Insert the verifying keys. - for (function_name, (verifying_key, _)) in deployment.verifying_keys() { + for (function_name, (verifying_key, _, _)) in deployment.verifying_keys() { stack.insert_verifying_key(function_name, verifying_key.clone())?; } lap!(timer, "Insert the verifying keys"); diff --git a/synthesizer/process/src/finalize.rs b/synthesizer/process/src/finalize.rs index 5b420ac064..04d482929b 100644 --- a/synthesizer/process/src/finalize.rs +++ b/synthesizer/process/src/finalize.rs @@ -38,7 +38,7 @@ impl Process { lap!(timer, "Compute the stack"); // Insert the verifying keys. - for (function_name, (verifying_key, _)) in deployment.verifying_keys() { + for (function_name, (verifying_key, _, _)) in deployment.verifying_keys() { stack.insert_verifying_key(function_name, verifying_key.clone())?; } lap!(timer, "Insert the verifying keys"); diff --git a/synthesizer/process/src/stack/deploy.rs b/synthesizer/process/src/stack/deploy.rs index 3836a799d7..fe36958976 100644 --- a/synthesizer/process/src/stack/deploy.rs +++ b/synthesizer/process/src/stack/deploy.rs @@ -37,6 +37,8 @@ impl Stack { let proving_key = self.get_proving_key(function_name)?; // Retrieve the verifying key. let verifying_key = self.get_verifying_key(function_name)?; + // Retrieve the function variables. + let variable_count = self.get_variable_count(function_name)?; lap!(timer, "Retrieve the keys for {function_name}"); // Certify the circuit. @@ -44,7 +46,7 @@ impl Stack { lap!(timer, "Certify the circuit"); // Add the verifying key and certificate to the bundle. - verifying_keys.push((*function_name, (verifying_key, certificate))); + verifying_keys.push((*function_name, (verifying_key, certificate, variable_count))); } finish!(timer); @@ -76,6 +78,9 @@ impl Stack { // Check that the number of combined constraints does not exceed the deployment limit. ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS); + // Check that the number of combined variables does not exceed the deployment limit. + ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES); + // Construct the call stacks and assignments used to verify the certificates. let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len()); @@ -92,7 +97,7 @@ impl Stack { ); // Iterate through the program functions and construct the callstacks and corresponding assignments. - for (function, (_, (verifying_key, _))) in + for (function, (_, (verifying_key, _, variable_limit))) in deployment.program().functions().values().zip_eq(deployment.verifying_keys()) { // Initialize a burner private key. @@ -132,20 +137,19 @@ impl Stack { lap!(timer, "Compute the request for {}", function.name()); // Initialize the assignments. let assignments = Assignments::::default(); - // Initialize the variable limit. - let variable_limit = N::MAX_DEPLOYMENT_VARIABLES / N::MAX_FUNCTIONS as u64; // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding. let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else { // Since a deployment must always pay non-zero fee, it must always have at least one constraint. bail!("The constraint limit of 0 for function '{}' is invalid", function.name()); }; + // Initialize the call stack. let call_stack = CallStack::CheckDeployment( vec![request], burner_private_key, assignments.clone(), Some(constraint_limit as u64), - Some(variable_limit), + Some(*variable_limit), ); // Append the function name, callstack, and assignments. call_stacks.push((function.name(), call_stack, assignments)); @@ -154,7 +158,7 @@ impl Stack { // Verify the certificates. let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(rng.gen())).collect::>(); cfg_into_iter!(call_stacks).zip_eq(deployment.verifying_keys()).zip_eq(rngs).try_for_each( - |(((function_name, call_stack, assignments), (_, (verifying_key, certificate))), mut rng)| { + |(((function_name, call_stack, assignments), (_, (verifying_key, certificate, _))), mut rng)| { // Synthesize the circuit. if let Err(err) = self.execute_function::(call_stack, caller, root_tvk, &mut rng) { bail!("Failed to synthesize the circuit for '{function_name}': {err}") diff --git a/synthesizer/process/src/stack/execute.rs b/synthesizer/process/src/stack/execute.rs index 9dca27e6b4..3024de0d84 100644 --- a/synthesizer/process/src/stack/execute.rs +++ b/synthesizer/process/src/stack/execute.rs @@ -144,14 +144,16 @@ impl StackExecute for Stack { ) -> Result> { let timer = timer!("Stack::execute_function"); + // Ensure constant circuit gadgets are initialized. + A::init_constants(); // Ensure the circuit environment is clean. A::reset(); // If in 'CheckDeployment' mode, set the constraint limit. // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those. - if let CallStack::CheckDeployment(_, _, _, constraint_limit, var_limit) = &call_stack { + if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack { A::set_constraint_limit(*constraint_limit); - A::set_variable_limit(*var_limit); + A::set_variable_limit(*variable_limit); } // Retrieve the next request. @@ -411,8 +413,6 @@ impl StackExecute for Stack { self.matches_value_type(output, output_type) })?; - println!("A::num_constants(): {}", A::num_constants()); - // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied. if matches!(registers.call_stack(), CallStack::Execute(..) | CallStack::PackageRun(..)) { // If the circuit is empty or not satisfied, then throw an error. @@ -426,7 +426,8 @@ impl StackExecute for Stack { } // Determine the number of variables allocated in the circuit. - let num_variables = A::num_variables(); + // NOTE: we can use this limit because our circuits only have a single scope. + let num_variables = A::num_variables_in_scope(); // Eject the circuit assignment and reset the circuit. let assignment = A::eject_assignment_and_reset(); @@ -438,7 +439,7 @@ impl StackExecute for Stack { // If the proving key does not exist, then synthesize it. if !self.contains_proving_key(function.name()) { // Add the circuit key to the mapping. - self.synthesize_from_assignment(function.name(), &assignment)?; + self.synthesize_from_assignment(function.name(), &assignment, num_variables)?; lap!(timer, "Synthesize the {} circuit key", function.name()); } } @@ -456,7 +457,6 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), - num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, @@ -479,7 +479,6 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), - num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, @@ -500,7 +499,6 @@ impl StackExecute for Stack { let metrics = CallMetrics { program_id: *self.program_id(), function_name: *function.name(), - num_variables, num_instructions: function.instructions().len(), num_request_constraints, num_function_constraints, diff --git a/synthesizer/process/src/stack/helpers/initialize.rs b/synthesizer/process/src/stack/helpers/initialize.rs index ea1c35f370..0340eb66ce 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(), + variable_counts: Default::default(), number_of_calls: Default::default(), finalize_costs: Default::default(), program_depth: 0, diff --git a/synthesizer/process/src/stack/helpers/synthesize.rs b/synthesizer/process/src/stack/helpers/synthesize.rs index 72d7d7bc24..e5532a7374 100644 --- a/synthesizer/process/src/stack/helpers/synthesize.rs +++ b/synthesizer/process/src/stack/helpers/synthesize.rs @@ -80,6 +80,8 @@ impl Stack { ensure!(self.contains_proving_key(function_name), "Function '{function_name}' is missing a proving key."); // Ensure the verifying key exists. ensure!(self.contains_verifying_key(function_name), "Function '{function_name}' is missing a verifying key."); + // Ensure the variable count exists. + ensure!(self.contains_variable_count(function_name), "Function '{function_name}' is missing variable count."); Ok(()) } @@ -89,6 +91,7 @@ impl Stack { &self, function_name: &Identifier, assignment: &circuit::Assignment, + variable_count: u64, ) -> Result<()> { // If the proving and verifying key already exist, skip the synthesis for this function. if self.contains_proving_key(function_name) && self.contains_verifying_key(function_name) { @@ -100,6 +103,8 @@ impl Stack { // Insert the proving key. self.insert_proving_key(function_name, proving_key)?; // Insert the verifying key. - self.insert_verifying_key(function_name, verifying_key) + self.insert_verifying_key(function_name, verifying_key)?; + // Insert the variable count. + self.insert_variable_count(function_name, variable_count) } } diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 070b1391dd..9b391080fa 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -186,6 +186,8 @@ pub struct Stack { proving_keys: Arc, ProvingKey>>>, /// The mapping of function name to verifying key. verifying_keys: Arc, VerifyingKey>>>, + /// The mapping of function name to number of variables. + variable_counts: Arc, u64>>>, /// The mapping of function names to the number of calls. number_of_calls: IndexMap, usize>, /// The mapping of function names to finalize cost. @@ -377,6 +379,12 @@ impl Stack { self.verifying_keys.read().contains_key(function_name) } + /// Returns `true` if the function variables for the given function name exists. + #[inline] + pub fn contains_variable_count(&self, function_name: &Identifier) -> bool { + self.variable_counts.read().contains_key(function_name) + } + /// Returns the proving key for the given function name. #[inline] pub fn get_proving_key(&self, function_name: &Identifier) -> Result> { @@ -399,6 +407,16 @@ impl Stack { } } + /// Returns the variable count for the given function name. + #[inline] + pub fn get_variable_count(&self, function_name: &Identifier) -> Result { + // Return the number of function variables, if it exists. + match self.variable_counts.read().get(function_name) { + Some(variable_count) => Ok(*variable_count), + None => bail!("Variable count not found for: {}/{function_name}", self.program.id()), + } + } + /// Inserts the given proving key for the given function name. #[inline] pub fn insert_proving_key(&self, function_name: &Identifier, proving_key: ProvingKey) -> Result<()> { @@ -427,6 +445,20 @@ impl Stack { Ok(()) } + /// Inserts the given variable count for the given function name. + #[inline] + pub fn insert_variable_count(&self, function_name: &Identifier, variable_count: u64) -> Result<()> { + // Ensure the function name exists in the program. + ensure!( + self.program.contains_function(function_name), + "Function '{function_name}' does not exist in program '{}'.", + self.program.id() + ); + // Insert the number of function variables. + self.variable_counts.write().insert(*function_name, variable_count); + Ok(()) + } + /// Removes the proving key for the given function name. #[inline] pub fn remove_proving_key(&self, function_name: &Identifier) { diff --git a/synthesizer/process/src/trace/call_metrics/mod.rs b/synthesizer/process/src/trace/call_metrics/mod.rs index 4a68280811..d45fec6d94 100644 --- a/synthesizer/process/src/trace/call_metrics/mod.rs +++ b/synthesizer/process/src/trace/call_metrics/mod.rs @@ -22,7 +22,6 @@ pub struct CallMetrics { pub program_id: ProgramID, pub function_name: Identifier, pub num_instructions: usize, - pub num_variables: u64, pub num_request_constraints: u64, pub num_function_constraints: u64, pub num_response_constraints: u64, diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index c516ec8550..87cd6eaf25 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -1031,7 +1031,7 @@ function a: // Note: `deployment_transaction_ids` is sorted lexicographically by transaction ID, so the order may change if we update internal methods. assert_eq!( deployment_transaction_ids, - vec![deployment_1.id(), deployment_4.id(), deployment_3.id(), deployment_2.id()], + vec![deployment_4.id(), deployment_3.id(), deployment_1.id(), deployment_2.id()], "Update me if serialization has changed" ); } @@ -1401,11 +1401,11 @@ function do: // Increase the number of constraints in the verifying keys. let mut vks_with_overreport = Vec::with_capacity(deployment.verifying_keys().len()); - for (id, (vk, cert)) in deployment.verifying_keys() { + for (id, (vk, cert, vars)) in deployment.verifying_keys() { let mut vk = vk.deref().clone(); vk.circuit_info.num_constraints += 1; let vk = VerifyingKey::new(Arc::new(vk)); - vks_with_overreport.push((*id, (vk, cert.clone()))); + vks_with_overreport.push((*id, (vk, cert.clone(), *vars))); } // Each additional constraint costs 25 microcredits, so we need to increase the fee by 25 microcredits. @@ -1465,11 +1465,11 @@ function do: // Decrease the number of constraints in the verifying keys. let mut vks_with_underreport = Vec::with_capacity(deployment.verifying_keys().len()); - for (id, (vk, cert)) in deployment.verifying_keys() { + for (id, (vk, cert, vars)) in deployment.verifying_keys() { let mut vk = vk.deref().clone(); vk.circuit_info.num_constraints -= 2; let vk = VerifyingKey::new(Arc::new(vk)); - vks_with_underreport.push((*id, (vk, cert.clone()))); + vks_with_underreport.push((*id, (vk, cert.clone(), *vars))); } // Create a new deployment transaction with the underreported verifying keys. @@ -1503,6 +1503,79 @@ function do: vm.add_next_block(&block).unwrap(); } + #[test] + fn test_deployment_variable_underreport() { + let rng = &mut TestRng::default(); + + // Initialize a private key. + let private_key = sample_genesis_private_key(rng); + let address = Address::try_from(&private_key).unwrap(); + + // Initialize the genesis block. + let genesis = sample_genesis_block(rng); + + // Initialize the VM. + let vm = sample_vm(); + // Update the VM. + vm.add_next_block(&genesis).unwrap(); + + // Deploy the base program. + let program = Program::from_str( + r" +program synthesis_underreport.aleo; + +function do: + input r0 as u32.private; + add r0 r0 into r1; + output r1 as u32.public;", + ) + .unwrap(); + + // Create the deployment transaction. + let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); + + // Destructure the deployment transaction. + let Transaction::Deploy(txid, program_owner, deployment, fee) = transaction else { + panic!("Expected a deployment transaction"); + }; + + // Decrease the number of reported variables in the verifying keys. + let mut vks_with_underreport = Vec::with_capacity(deployment.verifying_keys().len()); + for (id, (vk, cert, vars)) in deployment.verifying_keys() { + vks_with_underreport.push((*id, (vk.clone(), cert.clone(), vars - 2))); + } + + // Create a new deployment transaction with the underreported verifying keys. + let adjusted_deployment = + Deployment::new(deployment.edition(), deployment.program().clone(), vks_with_underreport).unwrap(); + let adjusted_transaction = Transaction::Deploy(txid, program_owner, Box::new(adjusted_deployment), fee); + + // Verify the deployment transaction. It should error when synthesizing the first variable over the vk limit. + let result = vm.check_transaction(&adjusted_transaction, None, rng); + assert!(result.is_err()); + + // Create a standard transaction + // Prepare the inputs. + let inputs = [ + Value::::from_str(&address.to_string()).unwrap(), + Value::::from_str("1u64").unwrap(), + ] + .into_iter(); + + // Execute. + let transaction = + vm.execute(&private_key, ("credits.aleo", "transfer_public"), inputs, None, 0, None, rng).unwrap(); + + // Check that the deployment transaction will be aborted if injected into a block. + let block = sample_next_block(&vm, &private_key, &[transaction, adjusted_transaction.clone()], rng).unwrap(); + + // Check that the block aborts the deployment transaction. + assert_eq!(block.aborted_transaction_ids(), &vec![adjusted_transaction.id()]); + + // Update the VM. + vm.add_next_block(&block).unwrap(); + } + #[test] #[ignore] fn test_deployment_memory_overload() { @@ -1891,7 +1964,7 @@ finalize transfer_public: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_483_583, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_475_583, "Update me if the initial balance changes."); // Check the balance of the `credits_wrapper` program. let balance = match vm @@ -1943,7 +2016,7 @@ finalize transfer_public: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_431_058, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_423_058, "Update me if the initial balance changes."); // Check the balance of the `credits_wrapper` program. let balance = match vm @@ -2082,7 +2155,7 @@ finalize transfer_public_as_signer: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_412_068, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_404_068, "Update me if the initial balance changes."); // Check the `credits_wrapper` program does not have any balance. let balance = vm @@ -2237,7 +2310,7 @@ finalize transfer_public_to_private: _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_996_924_681, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_996_916_681, "Update me if the initial balance changes."); // Check that the `credits_wrapper` program has a balance of 0. let balance = match vm diff --git a/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out b/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out index 51fb5b5e60..d71649dc35 100644 --- a/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out +++ b/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out @@ -26,7 +26,7 @@ outputs: test_rand.aleo/rand_chacha_check: outputs: - '{"type":"future","id":"818878742790741579153893179075772445872751227433677932822653185952935999557field","value":"{\n program_id: test_rand.aleo,\n function_name: rand_chacha_check,\n arguments: [\n 1field,\n true\n ]\n}"}' - speculate: the execution was accepted + speculate: the execution was rejected add_next_block: succeeded. additional: - child_outputs: From 1db96ddac722102daad10f92a0786fa09b18599a Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Mon, 15 Apr 2024 12:21:13 +0200 Subject: [PATCH 4/8] Add variable limit to testnet --- circuit/environment/src/testnet_circuit.rs | 37 +++++++++++++++++++++ circuit/network/src/testnet_v0.rs | 38 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/circuit/environment/src/testnet_circuit.rs b/circuit/environment/src/testnet_circuit.rs index d220a80d7e..3f600da10b 100644 --- a/circuit/environment/src/testnet_circuit.rs +++ b/circuit/environment/src/testnet_circuit.rs @@ -23,6 +23,7 @@ type Field = ::Field; thread_local! { static CONSTRAINT_LIMIT: Cell> = Cell::new(None); + static VARIABLE_LIMIT: Cell> = Cell::new(None); pub(super) static TESTNET_CIRCUIT: RefCell> = RefCell::new(R1CS::new()); static IN_WITNESS: Cell = Cell::new(false); static ZERO: LinearCombination = LinearCombination::zero(); @@ -53,6 +54,16 @@ impl Environment for TestnetCircuit { IN_WITNESS.with(|in_witness| { // Ensure we are not in witness mode. if !in_witness.get() { + // Ensure that we do not surpass the variable limit for the circuit. + VARIABLE_LIMIT.with(|variable_limit| { + if let Some(limit) = variable_limit.get() { + // NOTE: we can use this function because circuits only have a single scope. + // Once we have nested scopes, we will need to track the number of variables in each scope. + if Self::num_variables_in_scope() > limit { + Self::halt(format!("Surpassed the variable limit ({limit})")) + } + } + }); TESTNET_CIRCUIT.with(|circuit| match mode { Mode::Constant => circuit.borrow_mut().new_constant(value), Mode::Public => circuit.borrow_mut().new_public(value), @@ -202,6 +213,11 @@ impl Environment for TestnetCircuit { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros()) } + /// Returns the number of variables for the current scope. + fn num_variables_in_scope() -> u64 { + TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_variables_in_scope()) + } + /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope()) @@ -244,6 +260,16 @@ impl Environment for TestnetCircuit { CONSTRAINT_LIMIT.with(|current_limit| current_limit.replace(limit)); } + /// Returns the variable limit for the circuit, if one exists. + fn get_variable_limit() -> Option { + VARIABLE_LIMIT.with(|current_limit| current_limit.get()) + } + + /// Sets the variable limit for the circuit. + fn set_variable_limit(limit: Option) { + VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit)); + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { TESTNET_CIRCUIT.with(|circuit| { @@ -251,6 +277,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -258,6 +285,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); + assert_eq!(0, r1cs.num_variables_in_scope()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -269,12 +297,15 @@ impl Environment for TestnetCircuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); // Ensure the circuit is now empty. assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -288,11 +319,14 @@ impl Environment for TestnetCircuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -306,11 +340,14 @@ impl Environment for TestnetCircuit { IN_WITNESS.with(|in_witness| in_witness.replace(false)); // Reset the constraint limit. Self::set_constraint_limit(None); + // Reset the variable limit. + Self::set_variable_limit(None); // Reset the circuit. *circuit.borrow_mut() = R1CS::<::BaseField>::new(); assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); + assert_eq!(0, circuit.borrow().num_variables_in_scope()); assert_eq!(0, circuit.borrow().num_constraints()); }); } diff --git a/circuit/network/src/testnet_v0.rs b/circuit/network/src/testnet_v0.rs index d782fdfb28..03bc69c240 100644 --- a/circuit/network/src/testnet_v0.rs +++ b/circuit/network/src/testnet_v0.rs @@ -101,6 +101,29 @@ thread_local! { pub struct AleoTestnetV0; impl Aleo for AleoTestnetV0 { + /// Initializes all of the constants for the Aleo environment. + fn init_constants() { + GENERATOR_G.with(|_| ()); + ENCRYPTION_DOMAIN.with(|_| ()); + GRAPH_KEY_DOMAIN.with(|_| ()); + SERIAL_NUMBER_DOMAIN.with(|_| ()); + BHP_256.with(|_| ()); + BHP_512.with(|_| ()); + BHP_768.with(|_| ()); + BHP_1024.with(|_| ()); + KECCAK_256.with(|_| ()); + KECCAK_384.with(|_| ()); + KECCAK_512.with(|_| ()); + PEDERSEN_64.with(|_| ()); + PEDERSEN_128.with(|_| ()); + POSEIDON_2.with(|_| ()); + POSEIDON_4.with(|_| ()); + POSEIDON_8.with(|_| ()); + SHA3_256.with(|_| ()); + SHA3_384.with(|_| ()); + SHA3_512.with(|_| ()); + } + /// Returns the encryption domain as a constant field element. fn encryption_domain() -> Field { ENCRYPTION_DOMAIN.with(|domain| domain.clone()) @@ -436,6 +459,11 @@ impl Environment for AleoTestnetV0 { E::num_nonzeros() } + /// Returns the number of variables for the current scope. + fn num_variables_in_scope() -> u64 { + E::num_variables_in_scope() + } + /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { E::num_constants_in_scope() @@ -476,6 +504,16 @@ impl Environment for AleoTestnetV0 { E::set_constraint_limit(limit) } + /// Returns the variable limit for the circuit, if one exists. + fn get_variable_limit() -> Option { + E::get_variable_limit() + } + + /// Sets the constraint limit for the circuit. + fn set_variable_limit(limit: Option) { + E::set_variable_limit(limit) + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { E::inject_r1cs(r1cs) From 40534126475fb9ee7f0da03061a1564f94ef2fcf Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 16 Apr 2024 11:58:02 +0200 Subject: [PATCH 5/8] Count variables in the entire circuit - not just the scope --- circuit/environment/src/circuit.rs | 24 ++++++++++------------ circuit/environment/src/environment.rs | 6 +++--- circuit/environment/src/helpers/counter.rs | 9 -------- circuit/environment/src/helpers/r1cs.rs | 15 +++++++++----- circuit/environment/src/testnet_circuit.rs | 24 ++++++++++------------ circuit/network/src/testnet_v0.rs | 10 ++++----- circuit/network/src/v0.rs | 10 ++++----- synthesizer/process/src/stack/execute.rs | 3 +-- 8 files changed, 46 insertions(+), 55 deletions(-) diff --git a/circuit/environment/src/circuit.rs b/circuit/environment/src/circuit.rs index f524a2d77e..b54141f7c3 100644 --- a/circuit/environment/src/circuit.rs +++ b/circuit/environment/src/circuit.rs @@ -58,9 +58,7 @@ impl Environment for Circuit { // Ensure that we do not surpass the variable limit for the circuit. VARIABLE_LIMIT.with(|variable_limit| { if let Some(limit) = variable_limit.get() { - // NOTE: we can use this function because circuits only have a single scope. - // Once we have nested scopes, we will need to track the number of variables in each scope. - if Self::num_variables_in_scope() > limit { + if Self::num_variables() > limit { Self::halt(format!("Surpassed the variable limit ({limit})")) } } @@ -214,6 +212,11 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope()) } + /// Returns the total number of variables in the entire circuit. + fn num_variables() -> u64 { + CIRCUIT.with(|circuit| circuit.borrow().num_variables()) + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { CIRCUIT.with(|circuit| circuit.borrow().num_constants()) @@ -239,11 +242,6 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros()) } - /// Returns the number of variables in the entire circuit. - fn num_variables_in_scope() -> u64 { - CIRCUIT.with(|circuit| circuit.borrow().num_variables_in_scope()) - } - /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope()) @@ -303,7 +301,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -311,7 +309,7 @@ impl Environment for Circuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); - assert_eq!(0, r1cs.num_variables_in_scope()); + assert_eq!(0, r1cs.num_variables()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -331,7 +329,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -352,7 +350,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -373,7 +371,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); }); } diff --git a/circuit/environment/src/environment.rs b/circuit/environment/src/environment.rs index c0d2cd87fb..b82984ebd0 100644 --- a/circuit/environment/src/environment.rs +++ b/circuit/environment/src/environment.rs @@ -110,6 +110,9 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + /// Returns `true` if all constraints in the current scope are satisfied. fn is_satisfied_in_scope() -> bool; + /// Returns the total number of variables in the entire environment. + fn num_variables() -> u64; + /// Returns the number of constants in the entire environment. fn num_constants() -> u64; @@ -130,9 +133,6 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + (Self::num_constants(), Self::num_public(), Self::num_private(), Self::num_constraints(), Self::num_nonzeros()) } - /// Returns the number of variables for the current scope. - fn num_variables_in_scope() -> u64; - /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64; diff --git a/circuit/environment/src/helpers/counter.rs b/circuit/environment/src/helpers/counter.rs index 8f3559a20d..8631f95af1 100644 --- a/circuit/environment/src/helpers/counter.rs +++ b/circuit/environment/src/helpers/counter.rs @@ -24,7 +24,6 @@ pub(crate) struct Counter { constants: u64, public: u64, private: u64, - total_variables: u64, nonzeros: (u64, u64, u64), parents: Vec<(Scope, Vec>>, u64, u64, u64, (u64, u64, u64))>, } @@ -115,24 +114,16 @@ impl Counter { /// Increments the number of constants by 1. pub(crate) fn increment_constant(&mut self) { self.constants += 1; - self.total_variables += 1; } /// Increments the number of public variables by 1. pub(crate) fn increment_public(&mut self) { self.public += 1; - self.total_variables += 1; } /// Increments the number of private variables by 1. pub(crate) fn increment_private(&mut self) { self.private += 1; - self.total_variables += 1; - } - - /// Returns the number of variables in scope. - pub(crate) fn num_variables_in_scope(&self) -> u64 { - self.total_variables } /// Returns the number of constants in scope in scope. diff --git a/circuit/environment/src/helpers/r1cs.rs b/circuit/environment/src/helpers/r1cs.rs index 59b6a69701..e9f5ebaf67 100644 --- a/circuit/environment/src/helpers/r1cs.rs +++ b/circuit/environment/src/helpers/r1cs.rs @@ -29,6 +29,7 @@ pub struct R1CS { private: Vec>, constraints: Vec>>, counter: Counter, + num_variables: u64, nonzeros: (u64, u64, u64), } @@ -41,6 +42,7 @@ impl R1CS { private: Default::default(), constraints: Default::default(), counter: Default::default(), + num_variables: 1, nonzeros: (0, 0, 0), } } @@ -60,6 +62,7 @@ impl R1CS { let variable = Variable::Constant(Rc::new(value)); self.constants.push(variable.clone()); self.counter.increment_constant(); + self.num_variables += 1; variable } @@ -68,6 +71,7 @@ impl R1CS { let variable = Variable::Public(Rc::new((self.public.len() as u64, value))); self.public.push(variable.clone()); self.counter.increment_public(); + self.num_variables += 1; variable } @@ -76,6 +80,7 @@ impl R1CS { let variable = Variable::Private(Rc::new((self.private.len() as u64, value))); self.private.push(variable.clone()); self.counter.increment_private(); + self.num_variables += 1; variable } @@ -134,6 +139,11 @@ impl R1CS { self.counter.scope() } + /// Returns the total number of variables in the constraint system. + pub fn num_variables(&self) -> u64 { + self.num_variables + } + /// Returns the number of constants in the constraint system. pub fn num_constants(&self) -> u64 { self.constants.len() as u64 @@ -159,11 +169,6 @@ impl R1CS { self.nonzeros } - /// Returns the total number of variables for the current scope. - pub fn num_variables_in_scope(&self) -> u64 { - self.counter.num_variables_in_scope() - } - /// Returns the number of constants for the current scope. pub(crate) fn num_constants_in_scope(&self) -> u64 { self.counter.num_constants_in_scope() diff --git a/circuit/environment/src/testnet_circuit.rs b/circuit/environment/src/testnet_circuit.rs index 3f600da10b..424ac69a2e 100644 --- a/circuit/environment/src/testnet_circuit.rs +++ b/circuit/environment/src/testnet_circuit.rs @@ -57,9 +57,7 @@ impl Environment for TestnetCircuit { // Ensure that we do not surpass the variable limit for the circuit. VARIABLE_LIMIT.with(|variable_limit| { if let Some(limit) = variable_limit.get() { - // NOTE: we can use this function because circuits only have a single scope. - // Once we have nested scopes, we will need to track the number of variables in each scope. - if Self::num_variables_in_scope() > limit { + if Self::num_variables() > limit { Self::halt(format!("Surpassed the variable limit ({limit})")) } } @@ -188,6 +186,11 @@ impl Environment for TestnetCircuit { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().is_satisfied_in_scope()) } + /// Returns the total number of variables in the entire circuit. + fn num_variables() -> u64 { + TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_variables()) + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_constants()) @@ -213,11 +216,6 @@ impl Environment for TestnetCircuit { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_nonzeros()) } - /// Returns the number of variables for the current scope. - fn num_variables_in_scope() -> u64 { - TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_variables_in_scope()) - } - /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { TESTNET_CIRCUIT.with(|circuit| circuit.borrow().num_constants_in_scope()) @@ -277,7 +275,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -285,7 +283,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); - assert_eq!(0, r1cs.num_variables_in_scope()); + assert_eq!(0, r1cs.num_variables()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -305,7 +303,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -326,7 +324,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -347,7 +345,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables_in_scope()); + assert_eq!(0, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); }); } diff --git a/circuit/network/src/testnet_v0.rs b/circuit/network/src/testnet_v0.rs index 03bc69c240..5917ab90ff 100644 --- a/circuit/network/src/testnet_v0.rs +++ b/circuit/network/src/testnet_v0.rs @@ -434,6 +434,11 @@ impl Environment for AleoTestnetV0 { E::is_satisfied_in_scope() } + /// Returns the total number of variables in the entire circuit. + fn num_variables() -> u64 { + E::num_variables() + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { E::num_constants() @@ -459,11 +464,6 @@ impl Environment for AleoTestnetV0 { E::num_nonzeros() } - /// Returns the number of variables for the current scope. - fn num_variables_in_scope() -> u64 { - E::num_variables_in_scope() - } - /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { E::num_constants_in_scope() diff --git a/circuit/network/src/v0.rs b/circuit/network/src/v0.rs index 8cd66f3e74..0687634595 100644 --- a/circuit/network/src/v0.rs +++ b/circuit/network/src/v0.rs @@ -434,6 +434,11 @@ impl Environment for AleoV0 { E::is_satisfied_in_scope() } + /// Returns the total number of variables in the entire circuit. + fn num_variables() -> u64 { + E::num_variables() + } + /// Returns the number of constants in the entire circuit. fn num_constants() -> u64 { E::num_constants() @@ -459,11 +464,6 @@ impl Environment for AleoV0 { E::num_nonzeros() } - /// Returns the number of variables for the current scope. - fn num_variables_in_scope() -> u64 { - E::num_variables_in_scope() - } - /// Returns the number of constants for the current scope. fn num_constants_in_scope() -> u64 { E::num_constants_in_scope() diff --git a/synthesizer/process/src/stack/execute.rs b/synthesizer/process/src/stack/execute.rs index 3024de0d84..1e2cf80e86 100644 --- a/synthesizer/process/src/stack/execute.rs +++ b/synthesizer/process/src/stack/execute.rs @@ -426,8 +426,7 @@ impl StackExecute for Stack { } // Determine the number of variables allocated in the circuit. - // NOTE: we can use this limit because our circuits only have a single scope. - let num_variables = A::num_variables_in_scope(); + let num_variables = A::num_variables(); // Eject the circuit assignment and reset the circuit. let assignment = A::eject_assignment_and_reset(); From 84d05f8a8f30ad51d36ec77a63065cfdfed4790d Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Wed, 17 Apr 2024 10:00:20 +0200 Subject: [PATCH 6/8] Correctly assert the number of variables of inital circuits --- circuit/environment/src/circuit.rs | 10 +++++----- circuit/environment/src/testnet_circuit.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/circuit/environment/src/circuit.rs b/circuit/environment/src/circuit.rs index b54141f7c3..7c3b0fc367 100644 --- a/circuit/environment/src/circuit.rs +++ b/circuit/environment/src/circuit.rs @@ -301,7 +301,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -309,7 +309,7 @@ impl Environment for Circuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); - assert_eq!(0, r1cs.num_variables()); + assert_eq!(1, r1cs.num_variables()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -329,7 +329,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -350,7 +350,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -371,7 +371,7 @@ impl Environment for Circuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); }); } diff --git a/circuit/environment/src/testnet_circuit.rs b/circuit/environment/src/testnet_circuit.rs index 424ac69a2e..322d12ae33 100644 --- a/circuit/environment/src/testnet_circuit.rs +++ b/circuit/environment/src/testnet_circuit.rs @@ -275,7 +275,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Inject the R1CS instance. let r1cs = circuit.replace(r1cs); @@ -283,7 +283,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, r1cs.num_constants()); assert_eq!(1, r1cs.num_public()); assert_eq!(0, r1cs.num_private()); - assert_eq!(0, r1cs.num_variables()); + assert_eq!(1, r1cs.num_variables()); assert_eq!(0, r1cs.num_constraints()); }) } @@ -303,7 +303,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Return the R1CS instance. r1cs @@ -324,7 +324,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); // Convert the R1CS instance to an assignment. Assignment::from(r1cs) @@ -345,7 +345,7 @@ impl Environment for TestnetCircuit { assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); assert_eq!(0, circuit.borrow().num_private()); - assert_eq!(0, circuit.borrow().num_variables()); + assert_eq!(1, circuit.borrow().num_variables()); assert_eq!(0, circuit.borrow().num_constraints()); }); } From 9fb9a60a723d10d5ad5093df6fefe9df0c91fc6b Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Wed, 17 Apr 2024 10:01:27 +0200 Subject: [PATCH 7/8] Price in num variables to avoid DoS by cheap const programs --- console/network/src/lib.rs | 2 +- synthesizer/process/src/cost.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 9da16c6c6a..5f62a97f1b 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -116,7 +116,7 @@ pub trait Network: /// The cost in microcredits per byte for the deployment transaction. const DEPLOYMENT_FEE_MULTIPLIER: u64 = 1_000; // 1 millicredit per byte /// The cost in microcredits per constraint for the deployment transaction. - const SYNTHESIS_FEE_MULTIPLIER: u64 = 25; // 25 microcredits per constraint + const SYNTHESIS_FEE_MULTIPLIER: u64 = 12; // 12 microcredits per constraint /// The maximum number of constraints in a deployment. const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 20; // 1,048,576 constraints /// The maximum number of variables in a deployment. diff --git a/synthesizer/process/src/cost.rs b/synthesizer/process/src/cost.rs index f97a3bf45c..8f6132e6ca 100644 --- a/synthesizer/process/src/cost.rs +++ b/synthesizer/process/src/cost.rs @@ -31,6 +31,8 @@ pub fn deployment_cost(deployment: &Deployment) -> Result<(u64, ( let num_characters = u32::try_from(program_id.name().to_string().len())?; // Compute the number of combined constraints in the program. let num_combined_constraints = deployment.num_combined_constraints()?; + // Compute the number of combined variables in the program. + let num_combined_variables = deployment.num_combined_variables()?; // Compute the storage cost in microcredits. let storage_cost = size_in_bytes @@ -38,7 +40,7 @@ pub fn deployment_cost(deployment: &Deployment) -> Result<(u64, ( .ok_or(anyhow!("The storage cost computation overflowed for a deployment"))?; // Compute the synthesis cost in microcredits. - let synthesis_cost = num_combined_constraints * N::SYNTHESIS_FEE_MULTIPLIER; + let synthesis_cost = (num_combined_variables + num_combined_constraints) * N::SYNTHESIS_FEE_MULTIPLIER; // Compute the namespace cost in credits: 10^(10 - num_characters). let namespace_cost = 10u64 From 4a7940ea29778100018673a26eb2a0cdc742d2cb Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Wed, 17 Apr 2024 11:26:13 +0200 Subject: [PATCH 8/8] Update test expectations --- synthesizer/src/vm/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 87cd6eaf25..68793f96b8 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -1031,7 +1031,7 @@ function a: // Note: `deployment_transaction_ids` is sorted lexicographically by transaction ID, so the order may change if we update internal methods. assert_eq!( deployment_transaction_ids, - vec![deployment_4.id(), deployment_3.id(), deployment_1.id(), deployment_2.id()], + vec![deployment_1.id(), deployment_3.id(), deployment_2.id(), deployment_4.id()], "Update me if serialization has changed" ); } @@ -1964,7 +1964,7 @@ finalize transfer_public: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_475_583, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_403_803, "Update me if the initial balance changes."); // Check the balance of the `credits_wrapper` program. let balance = match vm @@ -2016,7 +2016,7 @@ finalize transfer_public: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_423_058, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_351_278, "Update me if the initial balance changes."); // Check the balance of the `credits_wrapper` program. let balance = match vm @@ -2155,7 +2155,7 @@ finalize transfer_public_as_signer: Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => *balance, _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_997_404_068, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_997_321_968, "Update me if the initial balance changes."); // Check the `credits_wrapper` program does not have any balance. let balance = vm @@ -2310,7 +2310,7 @@ finalize transfer_public_to_private: _ => panic!("Expected a valid balance"), }; - assert_eq!(balance, 182_499_996_916_681, "Update me if the initial balance changes."); + assert_eq!(balance, 182_499_996_794_967, "Update me if the initial balance changes."); // Check that the `credits_wrapper` program has a balance of 0. let balance = match vm