Skip to content

Commit 48be1a8

Browse files
authored
fix(aztec-nr): support empty notes and events (#24464)
We were accidentally rejecting these but there's not reason not to, and had inconsistent encode/decode behavior.
1 parent 5cd500e commit 48be1a8

2 files changed

Lines changed: 82 additions & 14 deletions

File tree

noir-projects/aztec-nr/aztec/src/messages/logs/event.nr

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ where
6262

6363
/// Decodes the plaintext from a private event message (i.e. one of type [`PRIVATE_EVENT_MSG_TYPE_ID`]).
6464
///
65-
/// Returns `None` if `msg_content` has too few fields. This plaintext is meant to have originated
66-
/// from [`encode_private_event_message`].
65+
/// Returns `None` if `msg_content` is too short to contain the reserved fields. This plaintext is meant to have
66+
/// originated from [`encode_private_event_message`].
6767
///
6868
/// Note that while [`encode_private_event_message`] returns a fixed-size array, this function takes a [`BoundedVec`]
6969
/// instead. This is because when decoding we're typically processing runtime-sized plaintexts, more specifically,
@@ -72,7 +72,7 @@ pub(crate) unconstrained fn decode_private_event_message(
7272
msg_metadata: u64,
7373
msg_content: BoundedVec<Field, MAX_MESSAGE_CONTENT_LEN>,
7474
) -> Option<(EventSelector, Field, BoundedVec<Field, MAX_EVENT_SERIALIZED_LEN>)> {
75-
if msg_content.len() <= PRIVATE_EVENT_MSG_PLAINTEXT_RESERVED_FIELDS_LEN {
75+
if msg_content.len() < PRIVATE_EVENT_MSG_PLAINTEXT_RESERVED_FIELDS_LEN {
7676
Option::none()
7777
} else {
7878
let event_type_id = EventSelector::from_field(msg_metadata as Field);
@@ -93,14 +93,14 @@ pub(crate) unconstrained fn decode_private_event_message(
9393

9494
mod test {
9595
use crate::{
96-
event::event_interface::EventInterface,
96+
event::{event_interface::EventInterface, EventSelector},
9797
messages::{
9898
encoding::decode_message,
9999
logs::event::{decode_private_event_message, encode_private_event_message},
100100
msg_type::PRIVATE_EVENT_MSG_TYPE_ID,
101101
},
102102
};
103-
use crate::protocol::traits::Serialize;
103+
use crate::protocol::traits::{FromField, Serialize};
104104
use crate::test::mocks::mock_event::MockEvent;
105105

106106
global VALUE: Field = 7;
@@ -125,15 +125,45 @@ mod test {
125125
assert_eq(serialized_event, BoundedVec::from_array(event.serialize()));
126126
}
127127

128+
#[derive(Serialize)]
129+
struct EmptyEvent {}
130+
131+
impl EventInterface for EmptyEvent {
132+
fn get_event_type_id() -> EventSelector {
133+
EventSelector::from_field(90)
134+
}
135+
}
136+
137+
#[test]
138+
unconstrained fn encode_decode_empty_event() {
139+
let message_plaintext = encode_private_event_message(EmptyEvent {}, RANDOMNESS);
140+
141+
let (msg_type_id, msg_metadata, msg_content) =
142+
decode_message(BoundedVec::from_array(message_plaintext)).unwrap();
143+
144+
assert_eq(msg_type_id, PRIVATE_EVENT_MSG_TYPE_ID);
145+
146+
let (event_type_id, randomness, serialized_event) =
147+
decode_private_event_message(msg_metadata, msg_content).unwrap();
148+
149+
assert_eq(event_type_id, EmptyEvent::get_event_type_id());
150+
assert_eq(randomness, RANDOMNESS);
151+
assert_eq(serialized_event.len(), 0);
152+
}
153+
128154
#[test]
129155
unconstrained fn decode_empty_content_returns_none() {
130156
let empty = BoundedVec::new();
131157
assert(decode_private_event_message(0, empty).is_none());
132158
}
133159

134160
#[test]
135-
unconstrained fn decode_with_only_reserved_fields_returns_none() {
136-
let content = BoundedVec::from_array([0]);
137-
assert(decode_private_event_message(0, content).is_none());
161+
unconstrained fn decode_succeeds_with_only_reserved_fields() {
162+
let content = BoundedVec::from_array([RANDOMNESS]);
163+
164+
let (_, randomness, serialized_event) = decode_private_event_message(0, content).unwrap();
165+
166+
assert_eq(randomness, RANDOMNESS);
167+
assert_eq(serialized_event.len(), 0);
138168
}
139169
}

noir-projects/aztec-nr/aztec/src/messages/logs/note.nr

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ where
7777

7878
/// Decodes the plaintext from a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).
7979
///
80-
/// Returns `None` if `msg_content` has too few fields. This plaintext is meant to have originated
81-
/// from [`encode_private_note_message`].
80+
/// Returns `None` if `msg_content` is too short to contain the reserved fields. This plaintext is meant to have
81+
/// originated from [`encode_private_note_message`].
8282
///
8383
/// Note that while [`encode_private_note_message`] returns a fixed-size array, this function takes a [`BoundedVec`]
8484
/// instead. This is because when decoding we're typically processing runtime-sized plaintexts, more specifically,
@@ -87,7 +87,7 @@ pub(crate) unconstrained fn decode_private_note_message(
8787
msg_metadata: u64,
8888
msg_content: BoundedVec<Field, MAX_MESSAGE_CONTENT_LEN>,
8989
) -> Option<(Field, AztecAddress, Field, Field, BoundedVec<Field, MAX_NOTE_PACKED_LEN>)> {
90-
if msg_content.len() <= PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN {
90+
if msg_content.len() < PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN {
9191
Option::none()
9292
} else {
9393
let note_type_id = msg_metadata as Field; // TODO: make note type id not be a full field
@@ -117,7 +117,7 @@ mod test {
117117
},
118118
note::note_interface::NoteType,
119119
};
120-
use crate::protocol::{address::AztecAddress, traits::{FromField, Packable}};
120+
use crate::protocol::{address::AztecAddress, traits::{FromField, Packable, ToField}};
121121
use crate::test::mocks::mock_note::MockNote;
122122

123123
global VALUE: Field = 7;
@@ -197,15 +197,53 @@ mod test {
197197
let _ = encode_private_note_message(note, OWNER, STORAGE_SLOT, RANDOMNESS);
198198
}
199199

200+
#[derive(Packable)]
201+
struct EmptyNote {}
202+
203+
impl NoteType for EmptyNote {
204+
fn get_id() -> Field {
205+
0
206+
}
207+
}
208+
209+
#[test]
210+
unconstrained fn encode_decode_empty_note() {
211+
let encoded = encode_private_note_message(EmptyNote {}, OWNER, STORAGE_SLOT, RANDOMNESS);
212+
let (msg_type_id, msg_metadata, msg_content) = decode_message(BoundedVec::from_array(encoded)).unwrap();
213+
214+
assert_eq(msg_type_id, PRIVATE_NOTE_MSG_TYPE_ID);
215+
216+
let (note_type_id, owner, storage_slot, randomness, packed_note) =
217+
decode_private_note_message(msg_metadata, msg_content).unwrap();
218+
219+
assert_eq(note_type_id, EmptyNote::get_id());
220+
assert_eq(owner, OWNER);
221+
assert_eq(storage_slot, STORAGE_SLOT);
222+
assert_eq(randomness, RANDOMNESS);
223+
assert_eq(packed_note.len(), 0);
224+
}
225+
200226
#[test]
201227
unconstrained fn decode_empty_content_returns_none() {
202228
let empty = BoundedVec::new();
203229
assert(decode_private_note_message(0, empty).is_none());
204230
}
205231

206232
#[test]
207-
unconstrained fn decode_with_only_reserved_fields_returns_none() {
208-
let content = BoundedVec::from_array([0, 0, 0]);
233+
unconstrained fn decode_with_fewer_than_reserved_fields_returns_none() {
234+
let content = BoundedVec::from_array([0, 0]);
209235
assert(decode_private_note_message(0, content).is_none());
210236
}
237+
238+
#[test]
239+
unconstrained fn decode_succeeds_with_only_reserved_fields() {
240+
let content = BoundedVec::from_array([OWNER.to_field(), STORAGE_SLOT, RANDOMNESS]);
241+
242+
let (_, owner, storage_slot, randomness, packed_note) = decode_private_note_message(0, content).unwrap();
243+
244+
assert_eq(owner, OWNER);
245+
assert_eq(storage_slot, STORAGE_SLOT);
246+
assert_eq(randomness, RANDOMNESS);
247+
assert_eq(packed_note.len(), 0);
248+
}
211249
}

0 commit comments

Comments
 (0)