Skip to content

Commit

Permalink
feat!: introduce UnconstrainedContext (#6752)
Browse files Browse the repository at this point in the history
Continuing the work from
AztecProtocol/aztec-packages#6442, this PR
further formalizes the notion of a top-level unconstrained execution
context by introducing a struct that represents it (instead of relying
on the unit type). Not only is this less cryptic, it also provides
access to data previously unavailable such as the current block number
and contract address, which we'll need for some unconstrained getters
like `SharedMutable`'s.

The macro functions could potentially be refactored somewhat now that
private, public and unconstrained are more similar, but I'm not sure we
want to invest much effort there so I made the change as small as
possible.
  • Loading branch information
nventuro authored and AztecBot committed Jun 4, 2024
1 parent 640e6b8 commit fbea45c
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 23 deletions.
3 changes: 3 additions & 0 deletions aztec/src/context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod inputs;
mod packed_returns;
mod private_context;
mod public_context;
mod unconstrained_context;

mod call_interfaces;
mod gas;

Expand All @@ -16,3 +18,4 @@ use private_context::PrivateContext;
use packed_returns::PackedReturns;
use public_context::PublicContext;
use public_context::FunctionReturns;
use unconstrained_context::UnconstrainedContext;
32 changes: 32 additions & 0 deletions aztec/src/context/unconstrained_context.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use dep::protocol_types::address::AztecAddress;

struct UnconstrainedContext {
block_number: u32,
contract_address: AztecAddress,
}

impl UnconstrainedContext {
fn new() -> Self {
// We could call these oracles on the getters instead of at creation, which makes sense given that they might
// not even be accessed. However any performance gains are minimal, and we'd rather fail early if a user
// incorrectly attempts to create an UnconstrainedContext in an environment in which these oracles are not
// available.
let block_number = block_number_oracle();
let contract_address = contract_address_oracle();
Self { block_number, contract_address }
}

fn block_number(self) -> u32 {
self.block_number
}

fn contract_address(self) -> AztecAddress {
self.contract_address
}
}

#[oracle(getContractAddress)]
fn contract_address_oracle() -> AztecAddress {}

#[oracle(getBlockNumber)]
fn block_number_oracle() -> u32 {}
4 changes: 2 additions & 2 deletions aztec/src/state_vars/private_immutable.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dep::protocol_types::{
constants::GENERATOR_INDEX__INITIALIZATION_NULLIFIER, hash::pedersen_hash
};

use crate::context::PrivateContext;
use crate::context::{PrivateContext, UnconstrainedContext};
use crate::note::{
lifecycle::create_note, note_getter::{get_note, view_notes}, note_interface::NoteInterface,
note_viewer_options::NoteViewerOptions
Expand Down Expand Up @@ -66,7 +66,7 @@ impl<Note> PrivateImmutable<Note, &mut PrivateContext> {
// docs:end:get_note
}

impl<Note> PrivateImmutable<Note, ()> {
impl<Note> PrivateImmutable<Note, UnconstrainedContext> {
// docs:start:is_initialized
unconstrained pub fn is_initialized(self) -> bool {
let nullifier = self.compute_initialization_nullifier();
Expand Down
4 changes: 2 additions & 2 deletions aztec/src/state_vars/private_mutable.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use dep::protocol_types::{
grumpkin_point::GrumpkinPoint, hash::pedersen_hash
};

use crate::context::PrivateContext;
use crate::context::{PrivateContext, UnconstrainedContext};
use crate::note::{
lifecycle::{create_note, destroy_note}, note_getter::{get_note, view_notes},
note_interface::NoteInterface, note_viewer_options::NoteViewerOptions
Expand Down Expand Up @@ -124,7 +124,7 @@ impl<Note> PrivateMutable<Note, &mut PrivateContext> {
// docs:end:get_note
}

impl<Note> PrivateMutable<Note, ()> {
impl<Note> PrivateMutable<Note, UnconstrainedContext> {
unconstrained pub fn is_initialized(self) -> bool {
let nullifier = self.compute_initialization_nullifier();
check_nullifier_exists(nullifier)
Expand Down
4 changes: 2 additions & 2 deletions aztec/src/state_vars/private_set.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dep::protocol_types::{
constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::read_request::ReadRequest,
grumpkin_point::GrumpkinPoint
};
use crate::context::{PrivateContext, PublicContext};
use crate::context::{PrivateContext, PublicContext, UnconstrainedContext};
use crate::note::{
constants::MAX_NOTES_PER_PAGE, lifecycle::{create_note, create_note_hash_from_public, destroy_note},
note_getter::{get_notes, view_notes}, note_getter_options::NoteGetterOptions,
Expand Down Expand Up @@ -85,7 +85,7 @@ impl<Note> PrivateSet<Note, &mut PrivateContext> {
// docs:end:get_notes
}

impl<Note> PrivateSet<Note, ()> {
impl<Note> PrivateSet<Note, UnconstrainedContext> {
// docs:start:view_notes
unconstrained pub fn view_notes<N, M>(
self,
Expand Down
13 changes: 8 additions & 5 deletions aztec/src/state_vars/public_immutable.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{context::PublicContext, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage};
use crate::{
context::{PublicContext, UnconstrainedContext}, oracle::{storage::{storage_read, storage_write}},
state_vars::storage::Storage
};
use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}};

// Just like SharedImmutable but without the ability to read from private functions.
Expand Down Expand Up @@ -54,11 +57,11 @@ impl <T> PublicImmutable<T, &mut PublicContext> {
// docs:end:public_immutable_struct_read
}

impl<T> PublicImmutable<T, ()> {
impl<T> PublicImmutable<T, UnconstrainedContext> {
pub fn read<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
// Note that this is the exact same implementation as for public execution, though it might change in the future
// since unconstrained execution might not rely on the same oracles as used for public execution (which
// transpile to AVM opcodes).
// This looks the same as the &mut PublicContext impl, but is actually very different. In public execution the
// storage read oracle gets transpiled to SLOAD opcodes, whereas in unconstrained execution the PXE returns
// historical data.
let fields = storage_read(self.storage_slot);
T::deserialize(fields)
}
Expand Down
10 changes: 5 additions & 5 deletions aztec/src/state_vars/public_mutable.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::context::PublicContext;
use crate::context::{PublicContext, UnconstrainedContext};
use crate::oracle::storage::storage_read;
use crate::oracle::storage::storage_write;
use dep::protocol_types::traits::{Deserialize, Serialize};
Expand Down Expand Up @@ -42,11 +42,11 @@ impl<T> PublicMutable<T, &mut PublicContext> {
// docs:end:public_mutable_struct_write
}

impl<T> PublicMutable<T, ()> {
impl<T> PublicMutable<T, UnconstrainedContext> {
pub fn read<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
// Note that this is the exact same implementation as for public execution, though it might change in the future
// since unconstrained execution might not rely on the same oracles as used for public execution (which
// transpile to AVM opcodes).
// This looks the same as the &mut PublicContext impl, but is actually very different. In public execution the
// storage read oracle gets transpiled to SLOAD opcodes, whereas in unconstrained execution the PXE returns
// historical data.
let fields = storage_read(self.storage_slot);
T::deserialize(fields)
}
Expand Down
6 changes: 3 additions & 3 deletions aztec/src/state_vars/shared_immutable.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
context::{PrivateContext, PublicContext}, oracle::{storage::{storage_read, storage_write}},
state_vars::storage::Storage
context::{PrivateContext, PublicContext, UnconstrainedContext},
oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage
};
use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}};

Expand Down Expand Up @@ -49,7 +49,7 @@ impl<T> SharedImmutable<T, &mut PublicContext> {
}
}

impl<T> SharedImmutable<T, ()> {
impl<T> SharedImmutable<T, UnconstrainedContext> {
pub fn read_public<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
let fields = storage_read(self.storage_slot);
T::deserialize(fields)
Expand Down
10 changes: 6 additions & 4 deletions value-note/src/balance_utils.nr
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use dep::aztec::note::{note_getter::view_notes, note_viewer_options::NoteViewerOptions};
use dep::aztec::state_vars::PrivateSet;
use dep::aztec::{
context::UnconstrainedContext, state_vars::PrivateSet,
note::{note_getter::view_notes, note_viewer_options::NoteViewerOptions}
};
use crate::value_note::ValueNote;

unconstrained pub fn get_balance(set: PrivateSet<ValueNote, ()>) -> Field {
unconstrained pub fn get_balance(set: PrivateSet<ValueNote, UnconstrainedContext>) -> Field {
get_balance_with_offset(set, 0)
}

unconstrained pub fn get_balance_with_offset(set: PrivateSet<ValueNote, ()>, offset: u32) -> Field {
unconstrained pub fn get_balance_with_offset(set: PrivateSet<ValueNote, UnconstrainedContext>, offset: u32) -> Field {
let mut balance = 0;
// docs:start:view_notes
let mut options = NoteViewerOptions::new();
Expand Down

0 comments on commit fbea45c

Please sign in to comment.