Skip to content

Commit caf0b5d

Browse files
authored
refactor: kernel circuit refactor (#20147)
- Change from `counter >= split_counter` to `counter > split_counter` - Use existing utils to get squash flags to build kept arrays - Remove if-else from general hash utils: having it in the general utils seems like a footgun. Moving the if-else to the parents that know if an item should be hashed or not. - Remove duplicate util `poseidon2_cheaper_variable_hash`: it's exactly the same as `poseidon2_hash_subarray` now that poseidon2 does not add an extra field for variable length input.
2 parents 44a8742 + e2ef7b1 commit caf0b5d

File tree

30 files changed

+804
-337
lines changed

30 files changed

+804
-337
lines changed

noir-projects/aztec-nr/aztec/src/messages/processing/log_retrieval_request.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::protocol::{address::AztecAddress, traits::Serialize};
22

33
/// A request for the `bulk_retrieve_logs` oracle to fetch either:
44
/// - a public log emitted by `contract_address` with `unsiloed_tag`
5-
/// - a private log with tag equal to `silo_private_log(unsiloed_tag, contract_address)`.
5+
/// - a private log with tag equal to `compute_siloed_private_log_first_field(contract_address, unsiloed_tag)`.
66
#[derive(Serialize)]
77
pub(crate) struct LogRetrievalRequest {
88
pub contract_address: AztecAddress,

noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/accumulated_data/assert_sorted_padded_transformed_array/check_padded_items.nr

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ use types::traits::Empty;
1919
///
2020
/// Note: Technically, values outside this range could be non-empty, since they are ignored. However, enforcing emptiness
2121
/// helps prevent unexpected output if the array is misconfigured.
22+
///
23+
/// Returns the number of padded items.
2224
pub unconstrained fn check_padded_items<T, let N: u32>(
2325
padded_items: [T; N],
2426
num_original_items: u32,
2527
capped_size: u32,
26-
)
28+
) -> u32
2729
where
2830
T: Empty,
2931
{
3032
let mut seen_empty = false;
33+
let mut num_padded_items = 0;
3134
for i in 0..N {
3235
let item_is_empty = padded_items[i].is_empty();
3336
if i < num_original_items | i >= capped_size {
@@ -41,8 +44,11 @@ where
4144
"non-empty padded items should be consecutive within the range [num_original_items, capped_size)",
4245
);
4346
seen_empty |= item_is_empty;
47+
num_padded_items += 1 * !item_is_empty as u32;
4448
}
4549
}
50+
51+
num_padded_items
4652
}
4753

4854
mod tests {
@@ -63,35 +69,36 @@ mod tests {
6369
}
6470
}
6571

66-
pub fn execute(self) {
72+
pub fn execute(self) -> u32 {
6773
// Safety: `check_padded_items` is supposed to be called only in unconstrained functions.
6874
unsafe {
69-
check_padded_items(self.padded_items, self.num_original_items, self.capped_size);
75+
check_padded_items(self.padded_items, self.num_original_items, self.capped_size)
7076
}
7177
}
7278
}
7379

7480
#[test]
7581
fn full_padded_items() {
7682
let builder = TestBuilder::new();
77-
builder.execute();
83+
assert_eq(builder.execute(), 3);
7884
}
7985

8086
#[test]
8187
fn partially_full_padded_items() {
8288
let mut builder = TestBuilder::new();
89+
let expected_num_padded_items = 3;
8390

8491
// One empty padded item.
8592
builder.capped_size = 6;
86-
builder.execute();
93+
assert_eq(builder.execute(), expected_num_padded_items);
8794

8895
// Two empty padded items.
8996
builder.capped_size = 7;
90-
builder.execute();
97+
assert_eq(builder.execute(), expected_num_padded_items);
9198

9299
// Three empty padded items.
93100
builder.capped_size = 8;
94-
builder.execute();
101+
assert_eq(builder.execute(), expected_num_padded_items);
95102
}
96103

97104
#[test]
@@ -100,7 +107,7 @@ mod tests {
100107

101108
builder.padded_items = [0, 0, 0, 0, 0, 0, 0, 0];
102109

103-
builder.execute();
110+
assert_eq(builder.execute(), 0);
104111
}
105112

106113
#[test(should_fail_with = "padded items should be empty outside of the range [num_original_items, capped_size)")]
@@ -109,7 +116,7 @@ mod tests {
109116

110117
builder.padded_items[0] = 99;
111118

112-
builder.execute();
119+
let _ = builder.execute();
113120
}
114121

115122
#[test(should_fail_with = "padded items should be empty outside of the range [num_original_items, capped_size)")]
@@ -118,7 +125,7 @@ mod tests {
118125

119126
builder.capped_size = 4;
120127

121-
builder.execute();
128+
let _ = builder.execute();
122129
}
123130

124131
#[test(should_fail_with = "padded items should be empty outside of the range [num_original_items, capped_size)")]
@@ -127,7 +134,7 @@ mod tests {
127134

128135
builder.padded_items[6] = 99;
129136

130-
builder.execute();
137+
let _ = builder.execute();
131138
}
132139

133140
#[test(should_fail_with = "non-empty padded items should be consecutive within the range [num_original_items, capped_size)")]
@@ -136,7 +143,7 @@ mod tests {
136143

137144
builder.padded_items[2] = 0;
138145

139-
builder.execute();
146+
let _ = builder.execute();
140147
}
141148

142149
#[test(should_fail_with = "non-empty padded items should be consecutive within the range [num_original_items, capped_size)")]
@@ -145,6 +152,6 @@ mod tests {
145152

146153
builder.padded_items[3] = 0;
147154

148-
builder.execute();
155+
let _ = builder.execute();
149156
}
150157
}

noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_output_composer.nr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use types::{
3232
validation_requests::PrivateValidationRequests,
3333
},
3434
address::AztecAddress,
35-
hash::{create_protocol_nullifier, silo_nullifier},
35+
hash::{compute_siloed_nullifier, create_protocol_nullifier},
3636
side_effect::Ordered,
3737
traits::Empty,
3838
utils::arrays::ClaimedLengthArray,
@@ -78,7 +78,10 @@ impl PrivateKernelCircuitOutputComposer {
7878
// If `first_nullifier_hint` equals the protocol nullifier, it is inserted at index 0. Otherwise, at least one
7979
// private call must emit a nullifier, and the hint should match the first nullifier emitted.
8080
let scoped_protocol_nullifier = create_protocol_nullifier(inputs.tx_request);
81-
let protocol_nullifier = silo_nullifier(scoped_protocol_nullifier);
81+
let protocol_nullifier = compute_siloed_nullifier(
82+
scoped_protocol_nullifier.contract_address,
83+
scoped_protocol_nullifier.innermost().value,
84+
);
8285
let should_inject_protocol_nullifier = inputs.first_nullifier_hint == protocol_nullifier;
8386
if should_inject_protocol_nullifier {
8487
// Safety: we should ordinarily use `push`, so that the length correctly updates, but we know this is the very

noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,69 @@ use types::{
2323
MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PRIVATE_LOGS_PER_TX,
2424
MAX_U32_VALUE, SIDE_EFFECT_MASKING_ADDRESS,
2525
},
26-
hash::{compute_unique_siloed_note_hash, silo_note_hash, silo_nullifier, silo_private_log},
26+
hash::{
27+
compute_note_nonce_and_unique_note_hash, compute_siloed_note_hash, compute_siloed_nullifier,
28+
compute_siloed_private_log,
29+
},
2730
side_effect::{Counted, Scoped},
2831
utils::arrays::ClaimedLengthArray,
2932
};
3033

34+
pub fn silo_note_hash(note_hash: Scoped<Counted<NoteHash>>) -> Scoped<Counted<NoteHash>> {
35+
let mut siloed = note_hash;
36+
siloed.inner.inner =
37+
compute_siloed_note_hash(note_hash.contract_address, note_hash.innermost());
38+
39+
// We set the contract address to zero to indicate that the note hash has been siloed.
40+
// The reset output validator will check the contract address of the first note hash to ensure that siloing has not
41+
// been performed in a previous reset.
42+
siloed.contract_address = AztecAddress::zero();
43+
44+
siloed
45+
}
46+
47+
pub fn uniquify_siloed_note_hash(
48+
siloed_note_hash: Scoped<Counted<NoteHash>>,
49+
first_nullifier: Field,
50+
index_in_tx: u32,
51+
) -> Scoped<Counted<NoteHash>> {
52+
let mut uniquified = siloed_note_hash;
53+
uniquified.inner.inner = compute_note_nonce_and_unique_note_hash(
54+
siloed_note_hash.inner.inner,
55+
first_nullifier,
56+
index_in_tx,
57+
);
58+
uniquified
59+
}
60+
61+
pub fn silo_nullifier(nullifier: Scoped<Counted<Nullifier>>) -> Scoped<Counted<Nullifier>> {
62+
let mut siloed = nullifier;
63+
siloed.inner.inner.value =
64+
compute_siloed_nullifier(nullifier.contract_address, nullifier.innermost().value);
65+
66+
// We set the contract address to zero to indicate that the nullifier has been siloed.
67+
// The reset output validator will check the contract address of the first nullifier to ensure that siloing has not
68+
// been performed in a previous reset.
69+
siloed.contract_address = AztecAddress::zero();
70+
71+
siloed
72+
}
73+
74+
pub fn silo_private_log(
75+
private_log: Scoped<Counted<PrivateLogData>>,
76+
) -> Scoped<Counted<PrivateLogData>> {
77+
let mut siloed = private_log;
78+
siloed.inner.inner.log =
79+
compute_siloed_private_log(private_log.contract_address, private_log.innermost().log);
80+
81+
// We set the contract address to zero to indicate that the private log has been siloed.
82+
// The reset output validator will check the contract address of the first private log to ensure that siloing has not
83+
// been performed in a previous reset.
84+
siloed.contract_address = AztecAddress::zero();
85+
86+
siloed
87+
}
88+
3189
pub struct ResetOutputComposer {
3290
pub previous_kernel: PrivateKernelCircuitPublicInputs,
3391
pub padded_side_effects: PaddedSideEffects,
@@ -142,19 +200,18 @@ impl ResetOutputComposer {
142200
// Calling this in the unconstrained context is not a problem. Since it doesn't matter if there are non-empty
143201
// items outside of the range [num_kept_note_hashes, NoteHashSiloingAmount). The values will simply be ignored.
144202
// Having the check here is useful to catch any bugs where the padded values are not configured correctly by mistake.
145-
check_padded_items(
203+
let num_padded_items = check_padded_items(
146204
padded_note_hashes,
147205
num_kept_note_hashes,
148206
NoteHashSiloingAmount,
149207
);
150208

151209
let mut padded_unique_siloed_sorted_kept_note_hashes = sorted_kept_note_hashes;
152210

153-
for i in 0..sorted_kept_note_hashes.array.len() {
211+
for i in 0..num_kept_note_hashes + num_padded_items {
154212
// Push padding:
155-
if (i >= num_kept_note_hashes)
213+
if i >= num_kept_note_hashes {
156214
// We only apply padding if the executor of this circuit provides padding as input:
157-
& (padded_note_hashes[i] != 0) {
158215
// We 'push' so that the length increments.
159216
// Note: beyond `NoteHashSiloingAmount`, these so-called "padded" note_hashes
160217
// will be empty, because we just called `check_padded_items`.
@@ -176,7 +233,7 @@ impl ResetOutputComposer {
176233
let siloed_note_hash = silo_note_hash(note_hash);
177234

178235
// Unique:
179-
let unique_siloed_note_hash = compute_unique_siloed_note_hash(
236+
let unique_siloed_note_hash = uniquify_siloed_note_hash(
180237
siloed_note_hash,
181238
self.previous_kernel.claimed_first_nullifier,
182239
i,
@@ -224,7 +281,7 @@ impl ResetOutputComposer {
224281
// This means the "revertible from private-land" note_hashes cannot be made unique in
225282
// private-land, since the uniqueness can only be derived in public-land, because the
226283
// revertible notes' indices (within the note_hashes of the tx) will only be known in
227-
// public-land. See `compute_unique_siloed_note_hash` for more explanation on how the
284+
// public-land. See `compute_note_nonce_and_unique_note_hash` for more explanation on how the
228285
// uniqueness (aka note_nonce) is derived.
229286
//
230287
// In summary:
@@ -239,18 +296,11 @@ impl ResetOutputComposer {
239296
self.previous_kernel.claimed_revertible_counter,
240297
);
241298

242-
padded_unique_siloed_sorted_kept_note_hashes.array[i].inner.inner = if is_non_revertible {
299+
padded_unique_siloed_sorted_kept_note_hashes.array[i] = if is_non_revertible {
243300
unique_siloed_note_hash
244301
} else {
245302
siloed_note_hash
246303
};
247-
248-
// Why is it doing this? Is the contract_address being overloaded to mean something else?
249-
// Oh, is it being used to say "This has already been siloed"?
250-
// TODO: Why not use an extra bool to convey that?
251-
// A contract address will be assigned to the padding within constrained-land (see validate_sorted_siloed_unique_padded_note_hashes).
252-
padded_unique_siloed_sorted_kept_note_hashes.array[i].contract_address =
253-
AztecAddress::zero();
254304
}
255305
padded_unique_siloed_sorted_kept_note_hashes
256306
}
@@ -270,19 +320,18 @@ impl ResetOutputComposer {
270320
// Calling this in the unconstrained context is not a problem. Since it doesn't matter if there are non-empty
271321
// items outside of the range [num_nullifiers, NullifierSiloingAmount). The values will simply be ignored.
272322
// Having the check here is useful to catch any bugs where the padded values are not configured correctly by mistake.
273-
check_padded_items(
323+
let num_padded_items = check_padded_items(
274324
padded_nullifiers,
275325
num_kept_nullifiers,
276326
NullifierSiloingAmount,
277327
);
278328

279329
let mut padded_siloed_sorted_kept_nullifiers = sorted_kept_nullifiers;
280330

281-
for i in 0..sorted_kept_nullifiers.array.len() {
331+
for i in 0..num_kept_nullifiers + num_padded_items {
282332
// Push padding:
283-
if (i >= num_kept_nullifiers)
333+
if i >= num_kept_nullifiers {
284334
// we only apply padding if the executor of this circuit provides padding as input:
285-
& (padded_nullifiers[i] != 0) {
286335
padded_siloed_sorted_kept_nullifiers.push(Nullifier {
287336
value: padded_nullifiers[i],
288337
note_hash: 0,
@@ -295,11 +344,8 @@ impl ResetOutputComposer {
295344
.scope(SIDE_EFFECT_MASKING_ADDRESS));
296345
}
297346

298-
// Silo:
299-
padded_siloed_sorted_kept_nullifiers.array[i].inner.inner.value =
300-
silo_nullifier(padded_siloed_sorted_kept_nullifiers.array[i]);
301-
// A contract address will be assigned to the padding within constrained-land (see validate_sorted_siloed_padded_nullifiers).
302-
padded_siloed_sorted_kept_nullifiers.array[i].contract_address = AztecAddress::zero();
347+
let nullifier = padded_siloed_sorted_kept_nullifiers.array[i];
348+
padded_siloed_sorted_kept_nullifiers.array[i] = silo_nullifier(nullifier);
303349
}
304350

305351
padded_siloed_sorted_kept_nullifiers
@@ -320,7 +366,7 @@ impl ResetOutputComposer {
320366
// Calling this in the unconstrained context is not a problem. Since it doesn't matter if there are non-empty
321367
// items outside of the range [num_private_logs, PrivateLogSiloingAmount). The values will simply be ignored.
322368
// Having the check here is useful to catch any bugs where the padded values are not configured correctly by mistake.
323-
check_padded_items(
369+
let num_padded_items = check_padded_items(
324370
padded_private_logs,
325371
num_kept_private_logs,
326372
PrivateLogSiloingAmount,
@@ -329,11 +375,10 @@ impl ResetOutputComposer {
329375
// The ordering of adjectives in this name is intentional (back to front).
330376
let mut padded_siloed_sorted_kept_private_logs = sorted_kept_private_logs;
331377

332-
for i in 0..sorted_kept_private_logs.array.len() {
378+
for i in 0..num_kept_private_logs + num_padded_items {
333379
// Pad:
334-
if (i >= num_kept_private_logs)
380+
if i >= num_kept_private_logs {
335381
// we only apply padding if the executor of this circuit provides padding as input:
336-
& (padded_private_logs[i].length != 0) {
337382
padded_siloed_sorted_kept_private_logs.push(PrivateLogData {
338383
log: padded_private_logs[i],
339384
note_hash_counter: 0, // Recall: this is only used to hint which note (if any) this log corresponds to.
@@ -346,11 +391,8 @@ impl ResetOutputComposer {
346391
.scope(SIDE_EFFECT_MASKING_ADDRESS));
347392
}
348393

349-
// Silo:
350-
padded_siloed_sorted_kept_private_logs.array[i].inner.inner.log =
351-
silo_private_log(padded_siloed_sorted_kept_private_logs.array[i]);
352-
// A contract address will be assigned to the padding within constrained-land (see validate_sorted_siloed_padded_private_logs).
353-
padded_siloed_sorted_kept_private_logs.array[i].contract_address = AztecAddress::zero();
394+
let private_log = padded_siloed_sorted_kept_private_logs.array[i];
395+
padded_siloed_sorted_kept_private_logs.array[i] = silo_private_log(private_log);
354396
}
355397

356398
padded_siloed_sorted_kept_private_logs

0 commit comments

Comments
 (0)