Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
7 changes: 6 additions & 1 deletion noir-projects/aztec-nr/aztec/src/macros/events.nr
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{
macros::utils::{compute_struct_selector, derive_serialize_if_not_implemented, get_trait_impl_method},
macros::utils::{
compute_struct_selector, derive_deserialize_if_not_implemented, derive_serialize_if_not_implemented,
get_trait_impl_method,
},
utils::cmap::CHashMap,
};
use std::panic;
Expand Down Expand Up @@ -47,11 +50,13 @@ pub comptime fn event(s: TypeDefinition) -> Quoted {
register_event_selector(event_selector, s.name());

let serialize_impl = derive_serialize_if_not_implemented(s);
let deserialize_impl = derive_deserialize_if_not_implemented(s);

s.add_attribute("abi(events)");

quote {
$event_interface_impl
$serialize_impl
$deserialize_impl
}
}
12 changes: 11 additions & 1 deletion noir-projects/aztec-nr/aztec/src/macros/utils.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::protocol::meta::derive_serialize;
use crate::protocol::meta::{derive_deserialize, derive_serialize};
use std::meta::{ctstring::AsCtString, unquote};

pub(crate) comptime fn is_fn_external(f: FunctionDefinition) -> bool {
Expand Down Expand Up @@ -164,3 +164,13 @@ pub comptime fn derive_serialize_if_not_implemented(s: TypeDefinition) -> Quoted
derive_serialize(s)
}
}

/// Generates a quote that implements `Deserialize` for a given struct `s`. If the struct already implements
/// `Deserialize`, we return an empty quote.
pub comptime fn derive_deserialize_if_not_implemented(s: TypeDefinition) -> Quoted {
Comment thread
benesjan marked this conversation as resolved.
Comment thread
benesjan marked this conversation as resolved.
Outdated
if s.as_type().implements(quote { crate::protocol::traits::Deserialize }.as_trait_constraint()) {
quote {}
} else {
derive_deserialize(s)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::event::{event_emission::emit_event_in_private, event_interface::EventInterface};
use crate::protocol::traits::Serialize;
use crate::event::event_emission::emit_event_in_private;
Comment thread
benesjan marked this conversation as resolved.
use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_event::MockEvent};

global VALUE: Field = 7;
Expand All @@ -16,14 +15,11 @@ unconstrained fn emit_and_discover_event() {

env.discover_event(event_message, recipient);

let events = crate::test::helpers::txe_oracles::get_private_events(
MockEvent::get_event_type_id(),
TestEnvironment::get_default_address(),
recipient,
);
let events: BoundedVec<MockEvent, _> =
crate::test::helpers::txe_oracles::get_private_events(TestEnvironment::get_default_address(), recipient);
Comment thread
benesjan marked this conversation as resolved.
Outdated

assert_eq(events.len(), 1);
assert_eq(events.get(0), BoundedVec::from_array(event.serialize()));
assert_eq(events.get(0), event);
}

#[test]
Expand All @@ -42,15 +38,12 @@ unconstrained fn emit_and_discover_multiple_events() {
env.discover_event(event_messages.0, recipient);
env.discover_event(event_messages.1, recipient);

let events = crate::test::helpers::txe_oracles::get_private_events(
MockEvent::get_event_type_id(),
TestEnvironment::get_default_address(),
recipient,
);
let events: BoundedVec<MockEvent, _> =
crate::test::helpers::txe_oracles::get_private_events(TestEnvironment::get_default_address(), recipient);

assert_eq(events.len(), 2);
assert(events.any(|ev| ev == BoundedVec::from_array(event.serialize())));
assert(events.any(|ev| ev == BoundedVec::from_array(other_event.serialize())));
assert(events.any(|ev| ev == event));
assert(events.any(|ev| ev == other_event));
}

#[test]
Expand All @@ -66,14 +59,11 @@ unconstrained fn emit_and_discover_same_event_multiple_times() {
env.discover_event(event_message, recipient);
env.discover_event(event_message, recipient);

let events = crate::test::helpers::txe_oracles::get_private_events(
MockEvent::get_event_type_id(),
TestEnvironment::get_default_address(),
recipient,
);
let events: BoundedVec<MockEvent, _> =
crate::test::helpers::txe_oracles::get_private_events(TestEnvironment::get_default_address(), recipient);

assert_eq(events.len(), 1);
assert_eq(events.get(0), BoundedVec::from_array(event.serialize()));
assert_eq(events.get(0), event);
}

#[test]
Expand All @@ -89,11 +79,8 @@ unconstrained fn emit_and_fail_to_discover_event_as_other_recipient() {

env.discover_event(event_message, recipient);

let events = crate::test::helpers::txe_oracles::get_private_events(
MockEvent::get_event_type_id(),
TestEnvironment::get_default_address(),
other_recipient,
);
let events: BoundedVec<MockEvent, _> =
crate::test::helpers::txe_oracles::get_private_events(TestEnvironment::get_default_address(), other_recipient);

assert_eq(events.len(), 0);
}
Expand All @@ -112,12 +99,9 @@ unconstrained fn emit_and_discover_event_multiple_recipients() {
env.discover_event(event_message, recipient);
env.discover_event(event_message, other_recipient);

let events = crate::test::helpers::txe_oracles::get_private_events(
MockEvent::get_event_type_id(),
TestEnvironment::get_default_address(),
recipient,
);
let events: BoundedVec<MockEvent, _> =
crate::test::helpers::txe_oracles::get_private_events(TestEnvironment::get_default_address(), recipient);

assert_eq(events.len(), 1);
assert_eq(events.get(0), BoundedVec::from_array(event.serialize()));
assert_eq(events.get(0), event);
}
25 changes: 16 additions & 9 deletions noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{context::inputs::PrivateContextInputs, event::EventSelector, test::helpers::utils::TestAccount};
use crate::{
context::inputs::PrivateContextInputs,
event::{event_interface::EventInterface, EventSelector},
test::helpers::utils::TestAccount,
};

use crate::protocol::{
abis::function_selector::FunctionSelector,
Expand All @@ -8,6 +12,7 @@ use crate::protocol::{
},
contract_instance::ContractInstance,
traits::{Deserialize, ToField},
utils::reader::Reader,
};

global MAX_PRIVATE_EVENTS_PER_TXE_QUERY: u32 = 5;
Expand Down Expand Up @@ -79,19 +84,21 @@ pub unconstrained fn get_last_tx_effects() -> (Field, BoundedVec<Field, MAX_NOTE
pub unconstrained fn get_default_address() -> AztecAddress {}

// experimental
pub(crate) unconstrained fn get_private_events(
selector: EventSelector,
pub unconstrained fn get_private_events<Event>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub unconstrained fn get_private_events<Event>(
/// Experimental - there may be breaking API changes.
pub unconstrained fn get_private_events<Event>(

We want to somehow flag this. I imagine something like get_private_events will very likely take some sort of filter or something.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're making this pub, we should also at the very least document that we only search for events in the last block.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the docs in f575bfa and this how they get rendered:

image

Do you like it?

BTW do you know if there is a way how the MAX_PRIVATE_EVENTS_PER_TXE_QUERY constant could get rendered there? I tried to Claude it but it says there is no way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I wishit were more obvious. Perhaps we could do horizontal separators with ---?

like
---
this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow. What is supposed to be more obvious? And where do you want me to place the separator?

contract_address: AztecAddress,
scope: AztecAddress,
) -> BoundedVec<BoundedVec<Field, MAX_EVENT_SERIALIZATION_LENGTH>, MAX_PRIVATE_EVENTS_PER_TXE_QUERY> {
// This is a workaround as Noir does not currently let us return nested structs with arrays. We instead return a
// raw multidimensional array in get_private_events_oracle and create the BoundedVecs here.

let (raw_array_storage, event_lengths, query_length) = get_private_events_oracle(selector, contract_address, scope);
) -> BoundedVec<Event, MAX_PRIVATE_EVENTS_PER_TXE_QUERY>
where
Event: EventInterface + Deserialize,
{
let selector = Event::get_event_type_id();
let (raw_array_storage, _event_lengths, query_length) =
get_private_events_oracle(selector, contract_address, scope);

let mut events = BoundedVec::new();
for i in 0..query_length {
events.push(BoundedVec::from_parts(raw_array_storage[i], event_lengths[i]));
let mut reader = Reader::new(raw_array_storage[i]);
events.push(Deserialize::stream_deserialize(&mut reader));
Comment thread
benesjan marked this conversation as resolved.
Comment thread
benesjan marked this conversation as resolved.
}

events
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/aztec-nr/aztec/src/test/mocks/mock_event.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

use crate::{event::{event_emission::NewEvent, event_interface::EventInterface, EventSelector}, oracle::random::random};

use crate::protocol::traits::{FromField, Serialize};
use crate::protocol::traits::{Deserialize, FromField, Serialize};

#[derive(Eq, Serialize)]
#[derive(Eq, Serialize, Deserialize)]
pub(crate) struct MockEvent {
pub(crate) value: Field,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ pub contract Token {
global RECURSIVE_TRANSFER_CALL_MAX_NOTES: u32 = 8;

#[event]
struct Transfer {
from: AztecAddress,
to: AztecAddress,
amount: u128,
pub struct Transfer {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to either make all of these pub, or suggest that they are made pub.

@benesjan benesjan Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented this in 908c4d6

Is that would you meant by suggesting making them pub, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that the event macro should do it, linking to the doc piece you just wrote. We don't need pub though do we?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was originally hesitant to make the event macro apply the pub there as it makes it feel a bit more magical (events are just structs but with some extra stuff) but given that by definition they are a piece of data to be consumed by "the world out of the contract" I came to agree that it makes sense to not be annoying here and just auto-expose it.

Addressed in 6da2825

But I have one question about this:

linking to the doc piece you just wrote

Isn't it strange to link from the event macro code docs to the doc piece I wrote given that the event macro docs are way more "low-level"? Or is that not what you meant?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event macro docs are not low level - users use them whenever they declare an event. They are the event concept users are exposed to. It makes sense to me to have the docs explaining these things there. Keep in mind we'd be explaining what the macro does, how to then emit these events (ie `self.emit) etc, we'd not explain how the macro works under the hood.

pub from: AztecAddress,
pub to: AztecAddress,
pub amount: u128,
}

// docs:start:storage_struct
Expand Down Expand Up @@ -243,7 +243,7 @@ pub contract Token {
self.storage.balances.at(from).add(change).deliver(MessageDelivery.ONCHAIN_UNCONSTRAINED);
self.storage.balances.at(to).add(amount).deliver(MessageDelivery.ONCHAIN_UNCONSTRAINED);

// We don't constrain encryption of the note log in `transfer` (unlike in `transfer_in_private`) because the transfer
// We don't constrain encryption of the event log here (unlike in `transfer_in_private`) because the transfer
// function is only designed to be used in situations where the event is not strictly necessary (e.g. payment to
// another person where the payment is considered to be successful when the other party successfully decrypts a
// note).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ unconstrained fn transfer_private() {
mint_amount - transfer_amount,
);
utils::check_private_balance(env, token_contract_address, recipient, transfer_amount);

utils::check_private_transfer_event(token_contract_address, owner, recipient, transfer_amount);
}

#[test]
Expand All @@ -32,6 +34,8 @@ unconstrained fn transfer_private_to_self() {

// Check balances
utils::check_private_balance(env, token_contract_address, owner, mint_amount);

utils::check_private_transfer_event(token_contract_address, owner, owner, transfer_amount);
}

#[test]
Expand All @@ -56,6 +60,13 @@ unconstrained fn transfer_private_to_non_deployed_account() {
mint_amount - transfer_amount,
);
utils::check_private_balance(env, token_contract_address, not_deployed, transfer_amount);

utils::check_private_transfer_event(
token_contract_address,
owner,
not_deployed,
transfer_amount,
);
}

#[test(should_fail_with = "Balance too low")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ pub unconstrained fn check_private_balance(
);
}

pub unconstrained fn check_private_transfer_event(
token_contract_address: AztecAddress,
from: AztecAddress,
to: AztecAddress,
amount: u128,
) {
let events = aztec::test::helpers::txe_oracles::get_private_events::<Token::Transfer>(
Comment thread
benesjan marked this conversation as resolved.
Outdated
token_contract_address,
to,
);

assert_eq(events.len(), 1);

let event = events.get(0);

assert_eq(event.from, from);
assert_eq(event.to, to);
assert_eq(event.amount, amount);
}

// While authwits authorize a contract to perform an action, spending private notes still requires
// knowledge of the note secrets. For private tokens, the note owner must be involved in the
// transaction -- they cannot simply give another user an authwit and have that user spend the notes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub contract TestLog {
macros::{events::event, functions::external, storage::storage},
messages::message_delivery::MessageDelivery,
oracle::random::random,
protocol::{address::AztecAddress, traits::{FromField, Serialize}},
protocol::{address::AztecAddress, traits::{Deserialize, FromField, Serialize}},
state_vars::{Owned, PrivateSet},
};
use field_note::FieldNote;
Expand All @@ -23,7 +23,7 @@ pub contract TestLog {
value3: u8,
}

#[derive(Serialize)]
#[derive(Deserialize, Serialize)]
struct NestedStruct {
a: Field,
b: Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ pub comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {
let deserialization_of_struct_members = params
.map(|(param_name, param_type, _): (Quoted, Type, Quoted)| {
quote {
let $param_name = <$param_type as Deserialize>::stream_deserialize(reader);
let $param_name = <$param_type as $crate::serialization::Deserialize>::stream_deserialize(reader);
Comment thread
benesjan marked this conversation as resolved.
}
})
.join(quote {});
Expand Down Expand Up @@ -275,7 +275,7 @@ pub comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {

fn deserialize(fields: [Field; Self::N]) -> Self {
let mut reader = $crate::reader::Reader::new(fields);
let result = Self::stream_deserialize(&mut reader);
let result = <Self as $crate::serialization::Deserialize>::stream_deserialize(&mut reader);
reader.finish();
result
}
Expand Down
Loading