Skip to content
Merged
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
9 changes: 6 additions & 3 deletions aztec-up/bin/0.0.1/aztec-install
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,13 @@ function install_aztec_up {

# Updates appropriate shell script to ensure the paths are in PATH.
function update_path_env_var {
# We need both:
# - $AZTEC_HOME/current/bin and $AZTEC_HOME/current/node_modules/.bin for version-specific tools
# We need:
# - $AZTEC_HOME/current/bin for version-specific tools (nargo, aztec, bb, ...)
# - $AZTEC_HOME/bin for shared tools like aztec-up
local path_line="export PATH=\"\$HOME/.aztec/current/bin:\$HOME/.aztec/current/node_modules/.bin:\$HOME/.aztec/bin:\$PATH\""
# - $AZTEC_HOME/current/node_modules/.bin is intentionally NOT added: the per-version installer
# symlinks the @aztec-owned bins into current/bin, so adding node_modules/.bin
# would only expose transitive npm deps (jest, tsc, ...) and shadow user tools.
local path_line="export PATH=\"\$HOME/.aztec/current/bin:\$HOME/.aztec/bin:\$PATH\""

# Determine the user's shell.
local shell_profile=""
Expand Down
6 changes: 4 additions & 2 deletions aztec-up/bin/0.0.1/aztec-up
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,10 @@ function cmd_env {

local version_dir="$AZTEC_HOME/versions/$resolved_version"

# Output PATH export with relevant bin directories
echo "export PATH=\"$version_dir/bin:$version_dir/node_modules/.bin:\$PATH\""
# Output PATH export with relevant bin directories.
# node_modules/.bin is intentionally excluded -- the per-version installer
# symlinks @aztec-owned bins into bin/, keeping transitive npm deps off PATH.
echo "export PATH=\"$version_dir/bin:\$PATH\""
}

# Main entry point
Expand Down
28 changes: 28 additions & 0 deletions aztec-up/bin/0.0.1/install
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,29 @@ function install_aztec_packages {
npm install @aztec/aztec@$VERSION @aztec/cli-wallet@$VERSION @aztec/bb.js@$VERSION --prefix "$version_path"
}

function symlink_aztec_bins {
set -euo pipefail
# Populate version_bin_path with symlinks to @aztec-owned bins only. Adding
# node_modules/.bin wholesale to PATH would shadow user-installed tools
# (jest, tsc, semver, ...) with Aztec's transitive dependencies.
local npm_bin_dir="$version_path/node_modules/.bin"
[ -d "$npm_bin_dir" ] || return 0

local bin_link bin_name target
for bin_link in "$npm_bin_dir"/*; do
[ -L "$bin_link" ] || continue
target=$(readlink "$bin_link")
# npm writes relative symlinks like ../@aztec/aztec/... for scoped packages.
[[ "$target" == ../@aztec/* ]] || continue
bin_name=$(basename "$bin_link")
if [ -e "$version_bin_path/$bin_name" ] && [ ! -L "$version_bin_path/$bin_name" ]; then
echo_yellow "refusing to overwrite non-symlink $bin_name; @aztec package bin collides with a hand-installed toolchain binary" >&2
exit 1
fi
ln -sfn "../node_modules/.bin/$bin_name" "$version_bin_path/$bin_name"
done
}

function main {
# Create version directory
mkdir -p "$version_bin_path"
Expand Down Expand Up @@ -228,6 +251,11 @@ function main {
echo -n "Installing aztec packages... "
dump_fail retry install_aztec_packages
echo_green "done."

# Expose only @aztec-owned bins on PATH (drops transitive npm deps).
echo -n "Making aztec commands available... "
dump_fail retry symlink_aztec_bins
echo_green "done."
}

main "$@"
Expand Down
18 changes: 18 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [CLI] `aztec-up` no longer exposes transitive npm bins on PATH

The `aztec-up` installer used to add `$HOME/.aztec/current/node_modules/.bin` to your shell `PATH`, which put ~40 transitive npm bins (`jest`, `tsc`, `tsserver`, `semver`, `uuid`, `json5`, ...) onto your interactive shell and silently shadowed your own installed versions of those tools. Only the seven `@aztec/*`-owned bins (`aztec`, `aztec-wallet`, `bb`, `bb-cli`, `blob-client`, `noir-codegen`, `txe`) are now exposed.

If you had an Aztec version installed before this release, your shell profile (`~/.bashrc` or `~/.zshrc`) still contains the old `PATH` line. Re-run the installer once (replacing `[VERSION]` with whichever toolchain version you're on, e.g. `4.2.0`) to replace it:

```bash
VERSION=[VERSION] bash -i <(curl -sL https://install.aztec.network)
```

Open a fresh shell and confirm the leak is gone:

```bash
echo $PATH
```

`$HOME/.aztec/current/node_modules/.bin` should no longer appear in the output. You'll also see your own `jest`, `tsc`, etc. again instead of the ones bundled with the Aztec toolchain.

### [Aztec.nr] `emit_private_log_unsafe` / `emit_raw_note_log_unsafe` are deprecated

`emit_private_log_unsafe` and `emit_raw_note_log_unsafe` are deprecated and will be removed in a future release. Migrate to the new `emit_private_log_vec_unsafe` / `emit_raw_note_log_vec_unsafe` functions, which take a `BoundedVec<Field, PRIVATE_LOG_CIPHERTEXT_LEN>` instead of the `(log: [Field; PRIVATE_LOG_CIPHERTEXT_LEN], length: u32)` pair.
Expand Down
28 changes: 14 additions & 14 deletions noir-projects/aztec-nr/aztec/src/macros/aztec.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use crate::{
dispatch::generate_public_dispatch,
emit_public_init_nullifier::generate_emit_public_init_nullifier,
internals_functions_generation::{create_fn_abi_exports, process_functions},
offchain_receive::{
OFFCHAIN_RECEIVE_FN_NAME, OFFCHAIN_RECEIVE_PARAM_NAME, offchain_receive_param_type,
OFFCHAIN_RECEIVE_RETURN_TYPE,
},
storage::STORAGE_LAYOUT_NAME,
utils::{is_fn_contract_library_method, is_fn_external, is_fn_internal, is_fn_test, module_has_storage},
},
Expand Down Expand Up @@ -249,17 +253,18 @@ comptime fn generate_sync_state(process_custom_message_option: Quoted, offchain_
///
/// For more details, see `aztec::messages::processing::offchain::receive`.
comptime fn generate_offchain_receive() -> Quoted {
let param_type = offchain_receive_param_type(quote { aztec });
let parameters_struct_name = f"{OFFCHAIN_RECEIVE_FN_NAME}_parameters".quoted_contents();
let abi_struct_name = f"{OFFCHAIN_RECEIVE_FN_NAME}_abi".quoted_contents();

quote {
pub struct offchain_receive_parameters {
pub messages: BoundedVec<
aztec::messages::processing::offchain::OffchainMessage,
aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,
>,
pub struct $parameters_struct_name {
pub $OFFCHAIN_RECEIVE_PARAM_NAME: $param_type,
}

#[abi(functions)]
pub struct offchain_receive_abi {
parameters: offchain_receive_parameters,
pub struct $abi_struct_name {
parameters: $parameters_struct_name,
}

/// Receives offchain messages into this contract's offchain inbox for subsequent processing.
Expand All @@ -270,14 +275,9 @@ comptime fn generate_offchain_receive() -> Quoted {
///
/// This function is automatically injected by the `#[aztec]` macro.
#[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]
unconstrained fn offchain_receive(
messages: BoundedVec<
aztec::messages::processing::offchain::OffchainMessage,
aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,
>,
) {
unconstrained fn $OFFCHAIN_RECEIVE_FN_NAME($OFFCHAIN_RECEIVE_PARAM_NAME: $param_type) -> $OFFCHAIN_RECEIVE_RETURN_TYPE {
let address = aztec::context::UtilityContext::new().this_address();
aztec::messages::processing::offchain::receive(address, messages);
aztec::messages::processing::offchain::receive(address, $OFFCHAIN_RECEIVE_PARAM_NAME);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ use crate::macros::{
create_private_self_call_stub, create_private_static_stub, create_private_stub,
create_public_self_call_static_stub, create_public_self_call_stub, create_public_self_enqueue_static_stub,
create_public_self_enqueue_stub, create_public_static_stub, create_public_stub, create_utility_stub,
create_utility_stub_from_parts,
},
internals_functions_generation::external_functions_registry,
offchain_receive::{
OFFCHAIN_RECEIVE_FN_NAME, OFFCHAIN_RECEIVE_PARAM_NAME, offchain_receive_param_type,
OFFCHAIN_RECEIVE_RETURN_TYPE,
},
utils::is_fn_view,
};

Expand Down Expand Up @@ -37,7 +42,12 @@ pub(crate) comptime fn generate_external_function_calls(m: Module) -> Quoted {
})
.join(quote {});

let utility_contract_methods = utility_functions.map(|function| create_utility_stub(function)).join(quote {});
// `offchain_receive` is injected into every contract by `#[aztec]` as quoted code rather than via the
// `#[external("utility")]` attribute that populates `UTILITY_REGISTRY`, so it doesn't appear in
// `utility_functions`. We append its stub so it shows up in the interface like any other utility.
let utility_stubs =
utility_functions.map(|function| create_utility_stub(function)).push_back(create_offchain_receive_stub());
let utility_contract_methods = utility_stubs.join(quote {});

quote {
$private_contract_methods
Expand All @@ -46,6 +56,16 @@ pub(crate) comptime fn generate_external_function_calls(m: Module) -> Quoted {
}
}

/// Builds the interface stub for the macro-injected `offchain_receive` utility. Pulls the signature
/// from `crate::macros::offchain_receive` so it stays in lockstep with `generate_offchain_receive`.
comptime fn create_offchain_receive_stub() -> Quoted {
let param_type = offchain_receive_param_type(quote { crate }).as_type();
let fn_parameters = @[(OFFCHAIN_RECEIVE_PARAM_NAME, param_type)];
let fn_return_type = OFFCHAIN_RECEIVE_RETURN_TYPE.as_type();

create_utility_stub_from_parts(OFFCHAIN_RECEIVE_FN_NAME, fn_parameters, fn_return_type)
}

/// Generates helper structs for convenient self-invocation of contract functions:
///
/// - `CallSelf`: Call your own private or public functions, e.g.: `self.call_self.some_private_function(args)`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@ comptime fn create_stub_base(f: FunctionDefinition) -> (Quoted, Quoted, Quoted,
// Unfortunately, the usage of macros makes it a bit of a black box. To actually view the target function, you
// could instead command+click on `MyImportedContract`, or you can just manually search it. If you want to view the
// noir code that gets generated by this macro, you can use `nargo expand` on your contract.
let fn_name = f.name();
let fn_parameters = f.parameters();
create_stub_base_from_parts(f.name(), f.parameters())
}

/// Variant of `create_stub_base` that takes raw `(name, parameters)` instead of a `FunctionDefinition`.
///
/// This lets macro-injected functions (e.g. `offchain_receive`) reuse the stub-generation machinery without needing a
/// `FunctionDefinition`.
pub(crate) comptime fn create_stub_base_from_parts(
fn_name: Quoted,
fn_parameters: [(Quoted, Type)],
) -> (Quoted, Quoted, Quoted, Quoted, u32, Quoted, u32, Field) {
let fn_parameters_list = fn_parameters.map(|(name, typ): (Quoted, Type)| quote { $name: $typ }).join(quote {,});

let (serialized_args_array_construction, serialized_args_array_len_quote, serialized_args_array_name) =
Expand All @@ -38,7 +47,7 @@ comptime fn create_stub_base(f: FunctionDefinition) -> (Quoted, Quoted, Quoted,

let fn_name_str = f"\"{fn_name}\"".quoted_contents();
let fn_name_len: u32 = unquote!(quote { $fn_name_str.as_bytes().len()});
let fn_selector: Field = compute_fn_selector(f.name(), f.parameters());
let fn_selector: Field = compute_fn_selector(fn_name, fn_parameters);

(
fn_name, fn_parameters_list, serialized_args_array_construction, serialized_args_array_name,
Expand Down Expand Up @@ -123,9 +132,17 @@ pub(crate) comptime fn create_public_static_stub(f: FunctionDefinition) -> Quote
}

pub(crate) comptime fn create_utility_stub(f: FunctionDefinition) -> Quoted {
create_utility_stub_from_parts(f.name(), f.parameters(), f.return_type())
}

/// Variant of `create_utility_stub` that takes raw `(name, parameters, return_type)` instead of `FunctionDefinition`.
pub(crate) comptime fn create_utility_stub_from_parts(
fn_name: Quoted,
fn_parameters: [(Quoted, Type)],
fn_return_type: Type,
) -> Quoted {
let (fn_name, fn_parameters_list, serialized_args_array_construction, serialized_args_array_name, serialized_args_array_len, fn_name_str, fn_name_len, fn_selector) =
create_stub_base(f);
let fn_return_type = f.return_type();
create_stub_base_from_parts(fn_name, fn_parameters);

quote {
pub fn $fn_name(self, $fn_parameters_list) -> aztec::context::calls::UtilityCall<$fn_name_len, $serialized_args_array_len, $fn_return_type> {
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/macros/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use aztec::{aztec, AztecConfig};
pub mod dispatch;
pub(crate) mod calls_generation;
pub(crate) mod emit_public_init_nullifier;
pub(crate) mod offchain_receive;
pub mod internals_functions_generation;
pub mod functions;
pub mod utils;
Expand Down
29 changes: 29 additions & 0 deletions noir-projects/aztec-nr/aztec/src/macros/offchain_receive.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Shared signature for the macro-injected `offchain_receive` utility.
//!
//! `offchain_receive` lives in two places: it is injected into every contract by the `#[aztec]`
//! macro (see `generate_offchain_receive` in `macros/aztec.nr`), and its interface stub is
//! generated so other contracts can call it (see `create_offchain_receive_stub` in
//! `macros/calls_generation/external_functions.nr`). Both sites must agree on the function's
//! name, parameter name, parameter type, and return type, or the selectors they compute would
//! diverge and runtime calls would fail silently.
//!
//! To keep them in lockstep, both sites pull the signature components from here.

/// Name of the injected utility function.
pub(crate) comptime global OFFCHAIN_RECEIVE_FN_NAME: Quoted = quote { offchain_receive };

/// Name of the single parameter.
pub(crate) comptime global OFFCHAIN_RECEIVE_PARAM_NAME: Quoted = quote { messages };

/// Return type of the function.
pub(crate) comptime global OFFCHAIN_RECEIVE_RETURN_TYPE: Quoted = quote { () };

/// The parameter type `BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL>`.
pub(crate) comptime fn offchain_receive_param_type(prefix: Quoted) -> Quoted {
quote {
BoundedVec<
$prefix::messages::processing::offchain::OffchainMessage,
$prefix::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,
>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub(crate) type OffchainInboxSync<Env> = unconstrained fn[Env](
/* contract_address */AztecAddress, /* scope */ AztecAddress) -> EphemeralArray<OffchainMessageWithContext>;

/// A message delivered via the `offchain_receive` utility function.
#[derive(Serialize, Deserialize)]
pub struct OffchainMessage {
/// The encrypted message payload.
pub ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::protocol::{
abis::function_selector::FunctionSelector,
address::AztecAddress,
traits::{Deserialize, Packable, Serialize},
traits::{Deserialize, FromField, Packable, Serialize},
};

use crate::{
Expand All @@ -15,9 +15,10 @@ use crate::{
discovery::{
ComputeNoteHash, ComputeNoteNullifier, CustomMessageHandler, process_message::process_message_plaintext,
},
encoding::MESSAGE_PLAINTEXT_LEN,
encoding::{MESSAGE_CIPHERTEXT_LEN, MESSAGE_PLAINTEXT_LEN},
logs::{event::encode_private_event_message, note::encode_private_note_message},
processing::{MessageContext, validate_and_store_enqueued_notes_and_events},
offchain_messages::OFFCHAIN_MESSAGE_IDENTIFIER,
processing::{MessageContext, offchain::OffchainMessage, validate_and_store_enqueued_notes_and_events},
},
note::{note_interface::{NoteHash, NoteType}, NoteMessage},
oracle::version::assert_compatible_oracle_version,
Expand Down Expand Up @@ -608,6 +609,63 @@ impl TestEnvironment {
T::deserialize(serialized_return_values)
}

/// Returns offchain messages emitted by the last top-level call into TXE.
///
/// The returned `BoundedVec<OffchainMessage, _>` can be passed directly to a contract's `offchain_receive`
/// utility function to ingest the messages into the recipient's private state.
///
/// Each returned message is populated with:
/// - `ciphertext`, `recipient`: decoded from the raw emitted payload.
/// - `tx_hash`: `Option::some(hash)` for tx-producing entry points (`call_private`,
/// `call_public`); `Option::none()` for tx-less entry points (`execute_utility`,
/// `private_context`, `utility_context`).
/// - `anchor_block_timestamp`: the timestamp of the block the top-level call was
/// anchored against, matching the value the production flow would produce.
///
/// Returns up to `MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY` messages. The caller is responsible for
/// splitting the result into batches that fit `MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL` before
/// forwarding each batch to `offchain_receive`.
///
/// # Example
///
/// ```noir
/// env.call_private(sender, Token::at(token).transfer_in_private_with_offchain_delivery(...));
/// let messages = env.offchain_messages();
/// let mut batch: BoundedVec<_, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();
/// for i in 0..messages.len() { batch.push(messages.get(i)); }
/// env.execute_utility(Token::at(token).offchain_receive(batch));
/// ```
pub unconstrained fn offchain_messages(
_self: Self,
) -> BoundedVec<OffchainMessage, txe_oracles::MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY> {
let raw_effects = txe_oracles::get_last_call_offchain_effects();
let txe_oracles::TXECallContext { tx_hash, anchor_block_timestamp } = txe_oracles::get_last_call_context();

let mut messages: BoundedVec<OffchainMessage, txe_oracles::MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY> =
BoundedVec::new();

for i in 0..raw_effects.len() {
let payload = raw_effects.get(i);

// The raw payload layout produced by `deliver_offchain_message` is:
// [OFFCHAIN_MESSAGE_IDENTIFIER, recipient, ...ciphertext]
// A call may also emit offchain effects that are *not* `OffchainMessage`s (e.g. authwit requests
// via `auth.nr::emit_offchain_effect`). We only concern ourselves with offchain messages here.
if payload.get(0) == OFFCHAIN_MESSAGE_IDENTIFIER {
let recipient = AztecAddress::from_field(payload.get(1));

let mut ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN> = BoundedVec::new();
for j in 0..MESSAGE_CIPHERTEXT_LEN {
ciphertext.push(payload.get(2 + j));
}

messages.push(OffchainMessage { ciphertext, recipient, tx_hash, anchor_block_timestamp });
}
}

messages
}

/// Performs a public contract function call, including the processing of any nested public calls. Returns the
/// result of the called function.
///
Expand Down
Loading
Loading