Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce deployment variable limit and cost #2431

Closed
wants to merge 8 commits into from
47 changes: 43 additions & 4 deletions circuit/environment/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Field = <console::MainnetV0 as console::Environment>::Field;

thread_local! {
pub(super) static CONSTRAINT_LIMIT: Cell<Option<u64>> = Cell::new(None);
pub(super) static VARIABLE_LIMIT: Cell<Option<u64>> = Cell::new(None);
pub(super) static CIRCUIT: RefCell<R1CS<Field>> = RefCell::new(R1CS::new());
pub(super) static IN_WITNESS: Cell<bool> = Cell::new(false);
pub(super) static ZERO: LinearCombination<Field> = LinearCombination::zero();
Expand Down Expand Up @@ -53,10 +54,22 @@ 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() {
// 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})"))
}
}
});
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")
Expand Down Expand Up @@ -226,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())
Expand Down Expand Up @@ -268,20 +286,32 @@ 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<u64> {
VARIABLE_LIMIT.with(|current_limit| current_limit.get())
}

/// Sets the variable limit for the circuit.
fn set_variable_limit(limit: Option<u64>) {
VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit));
}

/// Returns the R1CS circuit, resetting the circuit.
fn inject_r1cs(r1cs: R1CS<Self::BaseField>) {
CIRCUIT.with(|circuit| {
// Ensure the circuit is empty before injecting.
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);
// Ensure the circuit that was replaced is empty.
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());
})
}
Expand All @@ -293,12 +323,15 @@ 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::<<Self as Environment>::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
Expand All @@ -312,11 +345,14 @@ 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::<<Self as Environment>::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)
Expand All @@ -330,11 +366,14 @@ 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::<<Self as Environment>::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());
});
}
Expand Down
9 changes: 9 additions & 0 deletions circuit/environment/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,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;

Expand Down Expand Up @@ -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<u64>);

/// Returns the variable limit for the circuit, if one exists.
fn get_variable_limit() -> Option<u64>;

/// Sets the variable limit for the circuit.
fn set_variable_limit(limit: Option<u64>);

/// Returns the R1CS circuit, resetting the circuit.
fn inject_r1cs(r1cs: R1CS<Self::BaseField>);

Expand Down
9 changes: 9 additions & 0 deletions circuit/environment/src/helpers/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(crate) struct Counter<F: PrimeField> {
constants: u64,
public: u64,
private: u64,
total_variables: u64,
nonzeros: (u64, u64, u64),
parents: Vec<(Scope, Vec<Rc<Constraint<F>>>, u64, u64, u64, (u64, u64, u64))>,
}
Expand Down Expand Up @@ -114,16 +115,24 @@ impl<F: PrimeField> Counter<F> {
/// 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.
Expand Down
5 changes: 5 additions & 0 deletions circuit/environment/src/helpers/r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ impl<F: PrimeField> R1CS<F> {
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()
Expand Down
37 changes: 37 additions & 0 deletions circuit/environment/src/testnet_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Field = <console::TestnetV0 as console::Environment>::Field;

thread_local! {
static CONSTRAINT_LIMIT: Cell<Option<u64>> = Cell::new(None);
static VARIABLE_LIMIT: Cell<Option<u64>> = Cell::new(None);
pub(super) static TESTNET_CIRCUIT: RefCell<R1CS<Field>> = RefCell::new(R1CS::new());
static IN_WITNESS: Cell<bool> = Cell::new(false);
static ZERO: LinearCombination<Field> = LinearCombination::zero();
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -244,20 +260,32 @@ 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<u64> {
VARIABLE_LIMIT.with(|current_limit| current_limit.get())
}

/// Sets the variable limit for the circuit.
fn set_variable_limit(limit: Option<u64>) {
VARIABLE_LIMIT.with(|current_limit| current_limit.replace(limit));
}

/// Returns the R1CS circuit, resetting the circuit.
fn inject_r1cs(r1cs: R1CS<Self::BaseField>) {
TESTNET_CIRCUIT.with(|circuit| {
// Ensure the circuit is empty before injecting.
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);
// Ensure the circuit that was replaced is empty.
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());
})
}
Expand All @@ -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::<<Self as Environment>::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
Expand All @@ -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::<<Self as Environment>::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)
Expand All @@ -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::<<Self as Environment>::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());
});
}
Expand Down
3 changes: 3 additions & 0 deletions circuit/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <Self::Network as console::Network>::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<Self>;

Expand Down
38 changes: 38 additions & 0 deletions circuit/network/src/testnet_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
ENCRYPTION_DOMAIN.with(|domain| domain.clone())
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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<u64> {
E::get_variable_limit()
}

/// Sets the constraint limit for the circuit.
fn set_variable_limit(limit: Option<u64>) {
E::set_variable_limit(limit)
}

/// Returns the R1CS circuit, resetting the circuit.
fn inject_r1cs(r1cs: R1CS<Self::BaseField>) {
E::inject_r1cs(r1cs)
Expand Down
Loading