Skip to content

Commit 18895d5

Browse files
nchamoaztec-bot
authored andcommitted
feat(aztec-nr): new BoundedVec emit private log APIs (#22064)
1 parent 26e20c7 commit 18895d5

8 files changed

Lines changed: 79 additions & 34 deletions

File tree

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,33 @@ Aztec is in active development. Each version may introduce breaking changes that
99

1010
## TBD
1111

12+
### [Aztec.nr] `emit_private_log_unsafe` / `emit_raw_note_log_unsafe` are deprecated
13+
14+
`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.
15+
16+
```diff
17+
- context.emit_private_log_unsafe(tag, log, length);
18+
+ context.emit_private_log_vec_unsafe(tag, bounded_vec_log);
19+
- context.emit_raw_note_log_unsafe(tag, log, length, note_hash_counter);
20+
+ context.emit_raw_note_log_vec_unsafe(tag, bounded_vec_log, note_hash_counter);
21+
```
22+
23+
If you were manually padding an array and passing a shorter length, you can now create a `BoundedVec` from just the meaningful fields:
24+
25+
```diff
26+
- let padded = payload.concat([0; PRIVATE_LOG_CIPHERTEXT_LEN - 2]);
27+
- context.emit_private_log_unsafe(tag, padded, 2);
28+
+ let log = BoundedVec::from_array(payload);
29+
+ context.emit_private_log_vec_unsafe(tag, log);
30+
```
31+
32+
If you were passing the full array, wrap it with `BoundedVec::from_array`:
33+
34+
```diff
35+
- context.emit_private_log_unsafe(tag, ciphertext, ciphertext.len());
36+
+ context.emit_private_log_vec_unsafe(tag, BoundedVec::from_array(ciphertext));
37+
```
38+
1239
### [aztec-nr] Nullifier membership witness oracle returns split types
1340

1441
`get_nullifier_membership_witness` and `get_low_nullifier_membership_witness` now return `(NullifierLeafPreimage, MembershipWitness<NULLIFIER_TREE_HEIGHT>)` instead of the bundled `NullifierMembershipWitness` struct (which has been removed).

noir-projects/aztec-nr/aztec/src/context/private_context.nr

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,13 +909,24 @@ impl PrivateContext {
909909
/// happen to share a raw tag value become indistinguishable. Prefer the higher-level APIs
910910
/// ([`crate::messages::message_delivery::MessageDelivery`] for messages, `self.emit(event)` for events) which
911911
/// handle tagging automatically.
912+
// TODO(F-555): remove this function in favor of `emit_private_log_vec_unsafe`
913+
#[deprecated("use `emit_private_log_vec_unsafe` instead")]
912914
pub fn emit_private_log_unsafe(&mut self, tag: Field, log: [Field; PRIVATE_LOG_CIPHERTEXT_LEN], length: u32) {
913915
let counter = self.next_counter();
914916
let full_log = [tag].concat(log);
915917
self.private_logs.push(PrivateLogData { log: PrivateLog::new(full_log, length + 1), note_hash_counter: 0 }
916918
.count(counter));
917919
}
918920

921+
/// `BoundedVec`-based variant of [`emit_private_log_unsafe`](PrivateContext::emit_private_log_unsafe).
922+
///
923+
/// See [`emit_private_log_unsafe`](PrivateContext::emit_private_log_unsafe) for the full description of private
924+
/// log semantics.
925+
// TODO(F-555): once `emit_private_log_unsafe` is removed, rename this function to drop the `_vec` suffix.
926+
pub fn emit_private_log_vec_unsafe(&mut self, tag: Field, log: BoundedVec<Field, PRIVATE_LOG_CIPHERTEXT_LEN>) {
927+
self.emit_raw_note_log_vec_unsafe(tag, log, 0);
928+
}
929+
919930
// TODO: rename.
920931
/// Emits a private log that is explicitly tied to a newly-emitted note_hash, to convey to the kernel: "this log
921932
/// relates to this note".
@@ -938,6 +949,8 @@ impl PrivateContext {
938949
/// ## Safety
939950
///
940951
/// Same as [`PrivateContext::emit_private_log_unsafe`]: the `tag` should be domain-separated.
952+
// TODO(F-555): remove this function in favor of `emit_raw_note_log_vec_unsafe`
953+
#[deprecated("use `emit_raw_note_log_vec_unsafe` instead")]
941954
pub fn emit_raw_note_log_unsafe(
942955
&mut self,
943956
tag: Field,
@@ -951,6 +964,23 @@ impl PrivateContext {
951964
self.private_logs.push(private_log.count(counter));
952965
}
953966

967+
/// `BoundedVec`-based variant of [`emit_raw_note_log_unsafe`](PrivateContext::emit_raw_note_log_unsafe).
968+
///
969+
/// See [`emit_raw_note_log_unsafe`](PrivateContext::emit_raw_note_log_unsafe) for the full description of
970+
/// note-tied private log semantics.
971+
// TODO(F-555): once `emit_raw_note_log_unsafe` is removed, rename this function to drop the `_vec` suffix.
972+
pub fn emit_raw_note_log_vec_unsafe(
973+
&mut self,
974+
tag: Field,
975+
log: BoundedVec<Field, PRIVATE_LOG_CIPHERTEXT_LEN>,
976+
note_hash_counter: u32,
977+
) {
978+
let counter = self.next_counter();
979+
let full_log = [tag].concat(log.storage());
980+
let private_log = PrivateLogData { log: PrivateLog::new(full_log, log.len() + 1), note_hash_counter };
981+
self.private_logs.push(private_log.count(counter));
982+
}
983+
954984
/// Emits large data blobs.
955985
///
956986
/// This reuses the Contract Class Log channel to emit blobs of up to [`CONTRACT_CLASS_LOG_SIZE_IN_FIELDS`].

noir-projects/aztec-nr/aztec/src/messages/message_delivery.nr

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ pub global MessageDelivery: MessageDeliveryEnum =
192192
/// be created, there is no point in delivering the message.
193193
///
194194
/// `delivery_mode` must be one of [`MessageDeliveryEnum`].
195+
///
196+
/// ## Privacy
197+
///
198+
/// The emitted log always has the same length regardless of `MESSAGE_PLAINTEXT_LEN`, because all message ciphertexts
199+
/// also have the same length. This prevents accidental privacy leakage via the log length.
195200
pub fn do_private_message_delivery<Env, let MESSAGE_PLAINTEXT_LEN: u32>(
196201
context: &mut PrivateContext,
197202
encode_into_message_plaintext: fn[Env]() -> [Field; MESSAGE_PLAINTEXT_LEN],
@@ -211,6 +216,7 @@ pub fn do_private_message_delivery<Env, let MESSAGE_PLAINTEXT_LEN: u32>(
211216
let _constrained_tagging = delivery_mode == MessageDelivery.ONCHAIN_CONSTRAINED;
212217

213218
let contract_address = context.this_address();
219+
214220
let ciphertext = remove_constraints_if(
215221
!constrained_encryption,
216222
|| AES128::encrypt(encode_into_message_plaintext(), recipient, contract_address),
@@ -231,15 +237,13 @@ pub fn do_private_message_delivery<Env, let MESSAGE_PLAINTEXT_LEN: u32>(
231237
// we're emitting a note or non-note message.
232238
assert_constant(maybe_note_hash_counter.is_some());
233239

240+
let log = BoundedVec::from_array(ciphertext);
234241
if maybe_note_hash_counter.is_some() {
235242
// We associate the log with the note's side effect counter, so that if the note ends up being squashed in
236243
// the current transaction, the log will be removed as well.
237-
//
238-
// Note that the log always has the same length regardless of `MESSAGE_PLAINTEXT_LEN`, because all message
239-
// ciphertexts also have the same length. This prevents accidental privacy leakage via the log length.
240-
context.emit_raw_note_log_unsafe(log_tag, ciphertext, ciphertext.len(), maybe_note_hash_counter.unwrap());
244+
context.emit_raw_note_log_vec_unsafe(log_tag, log, maybe_note_hash_counter.unwrap());
241245
} else {
242-
context.emit_private_log_unsafe(log_tag, ciphertext, ciphertext.len());
246+
context.emit_private_log_vec_unsafe(log_tag, log);
243247
}
244248
}
245249
}

noir-projects/aztec-nr/uint-note/src/uint_note.nr

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ use aztec::{
1010
oracle::random::random,
1111
protocol::{
1212
address::AztecAddress,
13-
constants::{
14-
DOM_SEP__NOTE_COMPLETION_LOG_TAG, DOM_SEP__NOTE_HASH, DOM_SEP__PARTIAL_NOTE_VALIDITY_COMMITMENT,
15-
PRIVATE_LOG_CIPHERTEXT_LEN,
16-
},
13+
constants::{DOM_SEP__NOTE_COMPLETION_LOG_TAG, DOM_SEP__NOTE_HASH, DOM_SEP__PARTIAL_NOTE_VALIDITY_COMMITMENT},
1714
hash::{compute_log_tag, compute_siloed_nullifier, poseidon2_hash_with_separator},
1815
traits::{Deserialize, FromField, Hash, Packable, Serialize, ToField},
1916
},
@@ -171,8 +168,6 @@ pub struct PartialUintNote {
171168
}
172169
// docs:end:partial_uint_note_def
173170

174-
global NOTE_COMPLETION_PAYLOAD_LENGTH: u32 = 2;
175-
176171
impl PartialUintNote {
177172
/// Completes the partial note, creating a new note that can be used like any other UintNote.
178173
pub fn complete(self, context: PublicContext, completer: AztecAddress, storage_slot: Field, value: u128) {
@@ -228,8 +223,8 @@ impl PartialUintNote {
228223
// only done in private to hide the preimage of the hash that is inserted, but completed partial notes are
229224
// inserted in public as the public values are provided and the note hash computed.
230225
let log_tag = compute_log_tag(self.commitment, DOM_SEP__NOTE_COMPLETION_LOG_TAG);
231-
let padded_payload = self.compute_note_completion_payload_padded_for_private_log(storage_slot, value);
232-
context.emit_private_log_unsafe(log_tag, padded_payload, NOTE_COMPLETION_PAYLOAD_LENGTH);
226+
let payload = BoundedVec::from_array([storage_slot, value.to_field()]);
227+
context.emit_private_log_vec_unsafe(log_tag, payload);
233228
context.push_note_hash(self.compute_complete_note_hash(storage_slot, value));
234229
}
235230

@@ -245,15 +240,6 @@ impl PartialUintNote {
245240
)
246241
}
247242

248-
fn compute_note_completion_payload_padded_for_private_log(
249-
_self: Self,
250-
storage_slot: Field,
251-
value: u128,
252-
) -> [Field; PRIVATE_LOG_CIPHERTEXT_LEN] {
253-
let payload = [storage_slot, value.to_field()];
254-
payload.concat([0; PRIVATE_LOG_CIPHERTEXT_LEN - NOTE_COMPLETION_PAYLOAD_LENGTH])
255-
}
256-
257243
// docs:start:compute_complete_note_hash
258244
fn compute_complete_note_hash(self, storage_slot: Field, value: u128) -> Field {
259245
// Here we finalize the note hash by including the (public) storage slot and value into the partial note

noir-projects/noir-contracts/contracts/account/simulated_ecdsa_account_contract/src/main.nr

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ pub contract SimulatedEcdsaAccount {
3939
// and is therefore never nullified, so in practice the log will never be squashed. We
4040
// pass the note hash counter anyway for correctness.
4141
let dummy_log: [Field; MESSAGE_CIPHERTEXT_LEN] = [seed + 2; MESSAGE_CIPHERTEXT_LEN];
42-
self.context.emit_raw_note_log_unsafe(
42+
self.context.emit_raw_note_log_vec_unsafe(
4343
seed + 3,
44-
dummy_log,
45-
MESSAGE_CIPHERTEXT_LEN,
44+
BoundedVec::from_array(dummy_log),
4645
self.context.side_effect_counter,
4746
);
4847
}

noir-projects/noir-contracts/contracts/account/simulated_schnorr_account_contract/src/main.nr

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ pub contract SimulatedSchnorrAccount {
3939
// and is therefore never nullified, so in practice the log will never be squashed. We
4040
// pass the note hash counter anyway for correctness.
4141
let dummy_log: [Field; MESSAGE_CIPHERTEXT_LEN] = [seed + 2; MESSAGE_CIPHERTEXT_LEN];
42-
self.context.emit_raw_note_log_unsafe(
42+
self.context.emit_raw_note_log_vec_unsafe(
4343
seed + 3,
44-
dummy_log,
45-
MESSAGE_CIPHERTEXT_LEN,
44+
BoundedVec::from_array(dummy_log),
4645
self.context.side_effect_counter,
4746
);
4847
}

noir-projects/noir-contracts/contracts/test/benchmarking_contract/src/main.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ pub contract Benchmarking {
110110
let random_seed = unsafe { random() };
111111

112112
for i in 0..MAX_PRIVATE_LOGS_PER_CALL {
113-
let mut log = [0; PRIVATE_LOG_CIPHERTEXT_LEN];
113+
let mut log: BoundedVec<Field, PRIVATE_LOG_CIPHERTEXT_LEN> = BoundedVec::new();
114114
for j in 0..PRIVATE_LOG_CIPHERTEXT_LEN {
115-
log[j] = random_seed + (i * MAX_PRIVATE_LOGS_PER_CALL + j) as Field;
115+
log.push(random_seed + (i * MAX_PRIVATE_LOGS_PER_CALL + j) as Field);
116116
}
117-
self.context.emit_private_log_unsafe(0, log, PRIVATE_LOG_CIPHERTEXT_LEN);
117+
self.context.emit_private_log_vec_unsafe(0, log);
118118
}
119119
}
120120

noir-projects/noir-contracts/contracts/test/test_contract/src/main.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub contract Test {
3131
protocol::{
3232
abis::function_selector::FunctionSelector,
3333
address::{AztecAddress, EthAddress},
34-
constants::{PRIVATE_LOG_CIPHERTEXT_LEN, MAX_PUBLIC_LOG_SIZE_IN_FIELDS},
34+
constants::MAX_PUBLIC_LOG_SIZE_IN_FIELDS,
3535
traits::{Hash, Packable, Serialize},
3636
},
3737
// Event related
@@ -347,8 +347,8 @@ pub contract Test {
347347
self.call_self.emit_array_as_encrypted_log(tag, [0, 0, 0, 0, 0], owner, false);
348348

349349
// Emit a log with non-encrypted content for testing purpose.
350-
let leaky_log = event.serialize().concat([0; PRIVATE_LOG_CIPHERTEXT_LEN - 5]);
351-
self.context.emit_private_log_unsafe(tag, leaky_log, 5);
350+
let leaky_log = BoundedVec::from_array(event.serialize());
351+
self.context.emit_private_log_vec_unsafe(tag, leaky_log);
352352
}
353353
}
354354

0 commit comments

Comments
 (0)