From fbea45cb8bd7e6e7e7c5ff3d680998f9c1c105e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Mon, 3 Jun 2024 15:14:22 -0300 Subject: [PATCH] feat!: introduce UnconstrainedContext (#6752) Continuing the work from https://github.com/AztecProtocol/aztec-packages/pull/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. --- aztec/src/context.nr | 3 ++ aztec/src/context/unconstrained_context.nr | 32 ++++++++++++++++++++++ aztec/src/state_vars/private_immutable.nr | 4 +-- aztec/src/state_vars/private_mutable.nr | 4 +-- aztec/src/state_vars/private_set.nr | 4 +-- aztec/src/state_vars/public_immutable.nr | 13 +++++---- aztec/src/state_vars/public_mutable.nr | 10 +++---- aztec/src/state_vars/shared_immutable.nr | 6 ++-- value-note/src/balance_utils.nr | 10 ++++--- 9 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 aztec/src/context/unconstrained_context.nr diff --git a/aztec/src/context.nr b/aztec/src/context.nr index 4866291d..8c973d70 100644 --- a/aztec/src/context.nr +++ b/aztec/src/context.nr @@ -4,6 +4,8 @@ mod inputs; mod packed_returns; mod private_context; mod public_context; +mod unconstrained_context; + mod call_interfaces; mod gas; @@ -16,3 +18,4 @@ use private_context::PrivateContext; use packed_returns::PackedReturns; use public_context::PublicContext; use public_context::FunctionReturns; +use unconstrained_context::UnconstrainedContext; diff --git a/aztec/src/context/unconstrained_context.nr b/aztec/src/context/unconstrained_context.nr new file mode 100644 index 00000000..4af2e230 --- /dev/null +++ b/aztec/src/context/unconstrained_context.nr @@ -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 {} diff --git a/aztec/src/state_vars/private_immutable.nr b/aztec/src/state_vars/private_immutable.nr index 3c70465a..967df14e 100644 --- a/aztec/src/state_vars/private_immutable.nr +++ b/aztec/src/state_vars/private_immutable.nr @@ -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 @@ -66,7 +66,7 @@ impl PrivateImmutable { // docs:end:get_note } -impl PrivateImmutable { +impl PrivateImmutable { // docs:start:is_initialized unconstrained pub fn is_initialized(self) -> bool { let nullifier = self.compute_initialization_nullifier(); diff --git a/aztec/src/state_vars/private_mutable.nr b/aztec/src/state_vars/private_mutable.nr index 9e7934c3..b7e37162 100644 --- a/aztec/src/state_vars/private_mutable.nr +++ b/aztec/src/state_vars/private_mutable.nr @@ -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 @@ -124,7 +124,7 @@ impl PrivateMutable { // docs:end:get_note } -impl PrivateMutable { +impl PrivateMutable { unconstrained pub fn is_initialized(self) -> bool { let nullifier = self.compute_initialization_nullifier(); check_nullifier_exists(nullifier) diff --git a/aztec/src/state_vars/private_set.nr b/aztec/src/state_vars/private_set.nr index 3029df8b..0f844881 100644 --- a/aztec/src/state_vars/private_set.nr +++ b/aztec/src/state_vars/private_set.nr @@ -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, @@ -85,7 +85,7 @@ impl PrivateSet { // docs:end:get_notes } -impl PrivateSet { +impl PrivateSet { // docs:start:view_notes unconstrained pub fn view_notes( self, diff --git a/aztec/src/state_vars/public_immutable.nr b/aztec/src/state_vars/public_immutable.nr index 5d9ad2b7..b8f2c1d2 100644 --- a/aztec/src/state_vars/public_immutable.nr +++ b/aztec/src/state_vars/public_immutable.nr @@ -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. @@ -54,11 +57,11 @@ impl PublicImmutable { // docs:end:public_immutable_struct_read } -impl PublicImmutable { +impl PublicImmutable { pub fn read(self) -> T where T: Deserialize { - // 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) } diff --git a/aztec/src/state_vars/public_mutable.nr b/aztec/src/state_vars/public_mutable.nr index f3ce3a6d..69a6a0f8 100644 --- a/aztec/src/state_vars/public_mutable.nr +++ b/aztec/src/state_vars/public_mutable.nr @@ -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}; @@ -42,11 +42,11 @@ impl PublicMutable { // docs:end:public_mutable_struct_write } -impl PublicMutable { +impl PublicMutable { pub fn read(self) -> T where T: Deserialize { - // 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) } diff --git a/aztec/src/state_vars/shared_immutable.nr b/aztec/src/state_vars/shared_immutable.nr index 7662ec34..086c47ac 100644 --- a/aztec/src/state_vars/shared_immutable.nr +++ b/aztec/src/state_vars/shared_immutable.nr @@ -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}}; @@ -49,7 +49,7 @@ impl SharedImmutable { } } -impl SharedImmutable { +impl SharedImmutable { pub fn read_public(self) -> T where T: Deserialize { let fields = storage_read(self.storage_slot); T::deserialize(fields) diff --git a/value-note/src/balance_utils.nr b/value-note/src/balance_utils.nr index 5cbe5a4d..35b35d7e 100644 --- a/value-note/src/balance_utils.nr +++ b/value-note/src/balance_utils.nr @@ -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) -> Field { +unconstrained pub fn get_balance(set: PrivateSet) -> Field { get_balance_with_offset(set, 0) } -unconstrained pub fn get_balance_with_offset(set: PrivateSet, offset: u32) -> Field { +unconstrained pub fn get_balance_with_offset(set: PrivateSet, offset: u32) -> Field { let mut balance = 0; // docs:start:view_notes let mut options = NoteViewerOptions::new();