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

feat: refactor shared mutable to include private and unconstrained getters #7465

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,20 @@ impl UnconstrainedContext {
self.chain_id
}

unconstrained fn raw_storage_read<N>(self: Self, storage_slot: Field) -> [Field; N] {
storage_read(self.this_address(), storage_slot, self.block_number())
unconstrained fn raw_storage_read<N>(
self: Self,
address: AztecAddress,
storage_slot: Field
) -> [Field; N] {
storage_read(address, storage_slot, self.block_number())
}

unconstrained fn storage_read<T, N>(self, storage_slot: Field) -> T where T: Deserialize<N> {
T::deserialize(self.raw_storage_read(storage_slot))
unconstrained fn storage_read<T, N>(
self,
address: AztecAddress,
storage_slot: Field
) -> T where T: Deserialize<N> {
T::deserialize(self.raw_storage_read(address, storage_slot))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ impl <T, T_SERIALIZED_LEN> PublicImmutable<T, &mut PublicContext> where T: Seria

impl<T, T_SERIALIZED_LEN> PublicImmutable<T, UnconstrainedContext>where T: Deserialize<T_SERIALIZED_LEN> {
unconstrained pub fn read(self) -> T {
self.context.storage_read(self.storage_slot)
self.context.storage_read(self.context.this_address(), self.storage_slot)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ impl<T, T_SERIALIZED_LEN> PublicMutable<T, &mut PublicContext> where T: Serializ

impl<T, T_SERIALIZED_LEN> PublicMutable<T, UnconstrainedContext> where T: Deserialize<T_SERIALIZED_LEN> {
unconstrained pub fn read(self) -> T {
self.context.storage_read(self.storage_slot)
self.context.storage_read(self.context.this_address(), self.storage_slot)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<T, T_SERIALIZED_LEN> SharedImmutable<T, &mut PublicContext> where T: Serial

impl<T, T_SERIALIZED_LEN> SharedImmutable<T, UnconstrainedContext> where T: Serialize<T_SERIALIZED_LEN> + Deserialize<T_SERIALIZED_LEN> {
unconstrained pub fn read_public(self) -> T {
self.context.storage_read(self.storage_slot)
self.context.storage_read(self.context.this_address(), self.storage_slot)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dep::protocol_types::{
traits::{FromField, ToField}
};

use crate::context::{PrivateContext, PublicContext};
use crate::context::{PrivateContext, PublicContext, UnconstrainedContext};
use crate::state_vars::{
storage::Storage,
shared_mutable::{scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange}
Expand Down Expand Up @@ -171,6 +171,28 @@ impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, &mut PublicContext> where
self.read_delay_change().get_scheduled()
}

// The AVM currently does not support reading from other contract addresses
// pub fn get_current_value_in_public_other(context: &mut PublicContext, address: AztecAddress, storage_slot: Field) -> T {
// let block_number = context.block_number() as u32;

// let dummy = SharedMutable::new(context, storage_slot);

// dummy.read_value_change().get_current_at(block_number)
// }

// pub fn get_current_delay_in_public_other(context: &mut PublicContext, address: AztecAddress, storage_slot: Field) -> u32 {
// let block_number = context.block_number() as u32;
// self.read_delay_change().get_current(block_number)
// }

// pub fn get_scheduled_value_in_public_other(context: &mut PublicContext, address: AztecAddress, storage_slot: Field) -> (T, u32) {
// self.read_value_change().get_scheduled()
// }

// pub fn get_scheduled_delay_in_public_other(context: &mut PublicContext, address: AztecAddress, storage_slot: Field) -> (u32, u32) {
// self.read_delay_change().get_scheduled()
// }

fn read_value_change(self) -> ScheduledValueChange<T> {
self.context.storage_read(self.get_value_change_storage_slot())
}
Expand Down Expand Up @@ -199,14 +221,23 @@ impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, &mut PublicContext> where
}
}

impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, &mut PrivateContext> where T: ToField + FromField + Eq {
pub fn get_current_value_in_private(self) -> T {
impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, &mut PrivateContext> {
pub fn get_current_value_in_private(self) -> T where T: ToField + FromField + Eq {
SharedMutable::get_current_value_in_private_other::<T, INITIAL_DELAY>(self.context, self.context.this_address(), self.storage_slot)
}

pub fn get_current_value_in_private_other<T_OTHER, INITIAL_DELAY_OTHER>(
context: &mut PrivateContext,
address: AztecAddress,
storage_slot: Field
) -> T_OTHER where T_OTHER: FromField + ToField + Eq {
// When reading the current value in private we construct a historical state proof for the public value.
// However, since this value might change, we must constrain the maximum transaction block number as this proof
// will only be valid for however many blocks we can ensure the value will not change, which will depend on the
// current delay and any scheduled delay changes.
let dummy: SharedMutable<T_OTHER, INITIAL_DELAY_OTHER, ()> = SharedMutable::new((), storage_slot);

let (value_change, delay_change, historical_block_number) = self.historical_read_from_public_storage(self.context.get_header(), self.context.this_address());
let (value_change, delay_change, historical_block_number): (ScheduledValueChange<T_OTHER>, ScheduledDelayChange<INITIAL_DELAY_OTHER>, u32) = dummy.historical_read_from_public_storage(context.get_header(), address);

// We use the effective minimum delay as opposed to the current delay at the historical block as this one also
// takes into consideration any scheduled delay changes.
Expand All @@ -220,11 +251,33 @@ impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, &mut PrivateContext> wher

// We prevent this transaction from being included in any block after the block horizon, ensuring that the
// historical public value matches the current one, since it can only change after the horizon.
self.context.set_tx_max_block_number(block_horizon);
context.set_tx_max_block_number(block_horizon);
value_change.get_current_at(historical_block_number)
}
}

impl<T, INITIAL_DELAY> SharedMutable<T, INITIAL_DELAY, UnconstrainedContext> where T: ToField + FromField + Eq {
unconstrained pub fn get_current_value_in_unconstrained(self) -> T {
SharedMutable::get_current_value_in_unconstrained_other(self.context, self.context.this_address(), self.storage_slot)
}

unconstrained pub fn get_current_value_in_unconstrained_other(
context: UnconstrainedContext,
address: AztecAddress,
storage_slot: Field
) -> T {
let block_number = context.block_number() as u32;

let dummy = SharedMutable::new(context, storage_slot);

dummy.read_value_change(address).get_current_at(block_number)
}

fn read_value_change(self, address: AztecAddress) -> ScheduledValueChange<T> {
self.context.storage_read(address, self.get_value_change_storage_slot())
}
}

unconstrained fn get_public_storage_hints<T, INITIAL_DELAY>(
address: AztecAddress,
storage_slot: Field,
Expand Down
57 changes: 57 additions & 0 deletions noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,60 @@ fn test_get_current_value_in_private_bad_zero_hash_delay_hints() {

let _ = state_var.get_current_value_in_private();
}

#[test]
fn test_get_current_value_in_unconstrained_initial() {
let env = setup();
let state_var = in_unconstrained(env);

assert_eq(state_var.get_current_value_in_unconstrained(), zeroed());
}

#[test]
fn test_get_current_value_in_unconstrained_before_scheduled_change() {
let mut env = setup();
let state_var_public = in_public(env);

state_var_public.schedule_value_change(new_value);

let (_, block_of_change) = state_var_public.get_scheduled_value_in_public();

let original_value = zeroed();

let state_var_unconstrained = in_unconstrained(env);

// The current value has not changed
assert_eq(state_var_unconstrained.get_current_value_in_unconstrained(), original_value);

// The current value still does not change right before the block of change
env.advance_block_to(block_of_change - 1);
assert_eq(state_var_unconstrained.get_current_value_in_unconstrained(), original_value);
}

#[test]
fn test_get_current_value_in_unconstrained_at_scheduled_change() {
let mut env = setup();
let state_var_public = in_public(env);

state_var_public.schedule_value_change(new_value);

let (_, block_of_change) = state_var_public.get_scheduled_value_in_public();

env.advance_block_to(block_of_change);
let state_var_unconstrained = in_unconstrained(env);
assert_eq(state_var_unconstrained.get_current_value_in_unconstrained(), new_value);
}

#[test]
fn test_get_current_value_in_unconstrained_after_scheduled_change() {
let mut env = setup();
let state_var_public = in_public(env);

state_var_public.schedule_value_change(new_value);

let (_, block_of_change) = state_var_public.get_scheduled_value_in_public();

env.advance_block_to(block_of_change + 10);
let state_var_unconstrained = in_unconstrained(env);
assert_eq(state_var_unconstrained.get_current_value_in_unconstrained(), new_value);
}
18 changes: 17 additions & 1 deletion noir-projects/noir-contracts/contracts/test_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract Test {

use dep::aztec::note::constants::MAX_NOTES_PER_PAGE;

use dep::aztec::state_vars::{shared_mutable::SharedMutablePrivateGetter};
use dep::aztec::state_vars::{shared_mutable::{SharedMutable, SharedMutablePrivateGetter}};

use dep::aztec::{
context::inputs::private_context_inputs::PrivateContextInputs,
Expand Down Expand Up @@ -482,6 +482,22 @@ contract Test {
constant.value
}

// This function is used in the e2e_state_vars to test the SharedMutablePrivateGetter in isolation
#[aztec(private)]
fn test_shared_mutable_getter(
contract_address_to_read: AztecAddress,
storage_slot_of_shared_mutable: Field
) -> Field {
// It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly
let test = SharedMutable::get_current_value_in_private_other::<AztecAddress, 5>(
&mut context,
contract_address_to_read,
storage_slot_of_shared_mutable
);

test.to_field()
}

// This function is used in the e2e_state_vars to test the SharedMutablePrivateGetter in isolation
#[aztec(private)]
fn test_shared_mutable_private_getter(
Expand Down
Loading