Skip to content

Commit

Permalink
feat: Do not make unique revertible note hashes in the private kernels (
Browse files Browse the repository at this point in the history
#10524)

- Changes note_hash => unique => siloed to note_hash => siloed => unique
- Adds a new hint in the private kernels of wether the tx is private
only or not, verified in tail
- Makes unique only nonrevertible note hashes. Uses the previous hint to
know wether everything will be nonrevertible or if we need to look at
sideffect counters
- In the transitional adapters of the AVM simulator, makes unique
revertible note hashes from private

---------

Co-authored-by: MirandaWood <[email protected]>
  • Loading branch information
sirasistant and MirandaWood authored Dec 12, 2024
1 parent 333d6ce commit d327da1
Show file tree
Hide file tree
Showing 49 changed files with 6,737 additions and 6,201 deletions.
40 changes: 20 additions & 20 deletions noir-projects/aztec-nr/aztec/src/note/utils.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
};

use dep::protocol_types::hash::{
compute_siloed_note_hash as compute_siloed_note_hash,
compute_siloed_nullifier as compute_siloed_nullifier_from_preimage, compute_unique_note_hash,
compute_siloed_note_hash, compute_siloed_nullifier as compute_siloed_nullifier_from_preimage,
compute_unique_note_hash,
};

pub fn compute_siloed_nullifier<Note, let N: u32>(
Expand All @@ -29,13 +29,19 @@ where
Note: NoteInterface<N> + NullifiableNote,
{
let note_hash = note.compute_note_hash();
let nonce = note.get_header().nonce;
let counter = note.get_header().note_hash_counter;
let header = note.get_header();
let nonce = header.nonce;
let counter = header.note_hash_counter;

// If same tx note, read request always uses the normal note hash
if counter != 0 {
note_hash
} else {
compute_unique_note_hash(nonce, note_hash)
// If the note comes from a different tx, we need to compute the note hash that reached the tree
compute_unique_note_hash(
nonce,
compute_siloed_note_hash(header.contract_address, note_hash),
)
}
}

Expand All @@ -49,20 +55,14 @@ where
{
let header = note.get_header();

if header.note_hash_counter != 0 {
if header.nonce == 0 {
// Case 1: Transient note
note_hash_for_read_request
} else {
// Case 2: Non-revertible note, nullified by a revertible nullifier
let unique_note_hash =
compute_unique_note_hash(header.nonce, note_hash_for_read_request);
compute_siloed_note_hash(header.contract_address, unique_note_hash)
}
if (header.note_hash_counter != 0) & (header.nonce != 0) {
// Non-revertible note, nullified by a revertible nullifier, we need to nullify the note hash that will reach the tree
let siloed_note_hash =
compute_siloed_note_hash(header.contract_address, note_hash_for_read_request);

compute_unique_note_hash(header.nonce, siloed_note_hash)
} else {
// Case 3: Note from a previous transaction
// note_hash_for_read_request is already the unique_note_hash in this case
compute_siloed_note_hash(header.contract_address, note_hash_for_read_request)
note_hash_for_read_request
}
}

Expand Down Expand Up @@ -129,8 +129,8 @@ where
note.set_header(note_header);

let note_hash = note.compute_note_hash();
let unique_note_hash = compute_unique_note_hash(note_header.nonce, note_hash);
let siloed_note_hash = compute_siloed_note_hash(note_header.contract_address, unique_note_hash);
let siloed_note_hash = compute_siloed_note_hash(note_header.contract_address, note_hash);
let unique_note_hash = compute_unique_note_hash(note_header.nonce, siloed_note_hash);

let inner_nullifier = if compute_nullifier {
note.compute_nullifier_without_context()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ unconstrained fn main(
protocol_contract_tree_root: Field,
private_call: PrivateCallDataWithoutPublicInputs,
app_public_inputs: PrivateCircuitPublicInputs,
is_private_only: bool,
) -> pub PrivateKernelCircuitPublicInputs {
let private_inputs = PrivateKernelInitCircuitPrivateInputs::new(
tx_request,
vk_tree_root,
protocol_contract_tree_root,
private_call,
app_public_inputs,
is_private_only,
);
private_inputs.execute()
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ fn main(
vk_tree_root: Field,
protocol_contract_tree_root: Field,
private_call: PrivateCallDataWithoutPublicInputs,
is_private_only: bool,
app_public_inputs: call_data(1) PrivateCircuitPublicInputs,
) -> return_data PrivateKernelCircuitPublicInputs {
let private_inputs = PrivateKernelInitCircuitPrivateInputs::new(
Expand All @@ -17,6 +18,7 @@ fn main(
protocol_contract_tree_root,
private_call,
app_public_inputs,
is_private_only,
);
private_inputs.execute()
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@ impl PreviousKernelValidator {
}

pub fn validate_for_private_tail(self) {
assert(
self.previous_kernel.public_inputs.is_private_only,
"Must be private only to be processed in tail",
);
self.validate_common();
self.validate_empty_data();
}

pub fn validate_for_private_tail_to_public(self) {
assert(
!self.previous_kernel.public_inputs.is_private_only,
"Must not be private only to be processed in tail to public",
);
self.validate_common();
self.validate_non_empty_data();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ impl PrivateKernelCircuitOutputValidator {
private_call_array_lengths: PrivateCircuitPublicInputsArrayLengths,
vk_tree_root: Field,
protocol_contract_tree_root: Field,
is_private_only: bool,
) {
self.validate_initial_values(
tx_request,
private_call,
vk_tree_root,
protocol_contract_tree_root,
is_private_only,
);
let mut offsets = PrivateKernelCircuitPublicInputsArrayLengths::empty();
offsets.nullifiers = 1; // The first nullifier is not propagated from the private call.
Expand Down Expand Up @@ -77,8 +79,10 @@ impl PrivateKernelCircuitOutputValidator {
private_call: PrivateCircuitPublicInputs,
vk_tree_root: Field,
protocol_contract_tree_root: Field,
is_private_only: bool,
) {
// Constants.
assert_eq(self.output.is_private_only, is_private_only, "mismatch is_private_only");
assert_eq(self.output.constants.tx_context, tx_request.tx_context, "mismatch tx_context");
assert_eq(
self.output.constants.historical_header,
Expand Down Expand Up @@ -193,6 +197,11 @@ impl PrivateKernelCircuitOutputValidator {
previous_kernel: PrivateKernelCircuitPublicInputs,
array_lengths: PrivateKernelCircuitPublicInputsArrayLengths,
) {
assert_eq(
self.output.is_private_only,
previous_kernel.is_private_only,
"mismatch is_private_only",
);
assert_eq(self.output.constants, previous_kernel.constants, "mismatch constants");

assert_eq(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ impl PrivateKernelCircuitPublicInputsComposer {
private_call_public_inputs: PrivateCircuitPublicInputs,
vk_tree_root: Field,
protocol_contract_tree_root: Field,
is_private_only: bool,
) -> Self {
let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty();
public_inputs.is_private_only = is_private_only;

public_inputs.constants = TxConstantData {
historical_header: private_call_public_inputs.historical_header,
Expand All @@ -71,6 +73,7 @@ impl PrivateKernelCircuitPublicInputsComposer {
) -> Self {
let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty();

public_inputs.is_private_only = previous_kernel_public_inputs.is_private_only;
public_inputs.constants = previous_kernel_public_inputs.constants;
public_inputs.min_revertible_side_effect_counter =
previous_kernel_public_inputs.min_revertible_side_effect_counter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use dep::types::{
},
address::AztecAddress,
constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PRIVATE_LOGS_PER_TX},
hash::{compute_siloed_private_log_field, silo_note_hash, silo_nullifier},
hash::{
compute_siloed_private_log_field, compute_unique_siloed_note_hash, silo_note_hash,
silo_nullifier,
},
utils::arrays::sort_by_counter_asc,
};

Expand Down Expand Up @@ -73,10 +76,23 @@ impl<let NH_RR_PENDING: u32, let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, le
unconstrained fn get_sorted_siloed_note_hashes(
self,
) -> [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX] {
let mut note_hashes = sort_by_counter_asc(self.hints.kept_note_hashes);
let is_private_only = self.previous_kernel.is_private_only;
let min_revertible_side_effect_counter =
self.previous_kernel.min_revertible_side_effect_counter;
let first_nullifier = self.previous_kernel.end.nullifiers[0].value();
let mut note_hashes = sort_by_counter_asc(self.hints.kept_note_hashes);
for i in 0..note_hashes.len() {
note_hashes[i].note_hash.value = silo_note_hash(note_hashes[i], first_nullifier, i);
let note_hash = note_hashes[i];
let siloed_note_hash = silo_note_hash(note_hash);
let unique_note_hash =
compute_unique_siloed_note_hash(siloed_note_hash, first_nullifier, i);
// We don't silo with nonce revertible note hashes, since we don't know their final position in the tx
note_hashes[i].note_hash.value = if is_private_only
| (note_hash.counter() < min_revertible_side_effect_counter) {
unique_note_hash
} else {
siloed_note_hash
};
note_hashes[i].contract_address = AztecAddress::zero();
}
note_hashes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use dep::types::{
side_effect::scoped::Scoped,
},
constants::PRIVATE_LOG_SIZE_IN_FIELDS,
hash::{compute_siloed_private_log_field, silo_note_hash, silo_nullifier},
hash::{
compute_siloed_private_log_field, compute_unique_siloed_note_hash, silo_note_hash,
silo_nullifier,
},
traits::is_empty,
utils::arrays::assert_sorted_transformed_value_array_capped_size,
};
Expand Down Expand Up @@ -77,6 +80,7 @@ impl<let NH_RR_PENDING: u32, let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, le
}

fn validate_unchanged_data(self) {
assert_eq(self.output.is_private_only, self.previous_kernel.is_private_only);
assert_eq(self.output.constants, self.previous_kernel.constants);

assert_eq(
Expand Down Expand Up @@ -173,7 +177,7 @@ impl<let NH_RR_PENDING: u32, let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, le

fn validate_sorted_siloed_note_hashes(self) {
// Check that the values are not already siloed in a previous reset.
// Note hashes need to be siloed alltogether because new note hashes added later might affect the ordering and result in wrong nonces.
// Note hashes need to be siloed all together because new note hashes added later might affect the ordering and result in wrong nonces.
// We only need to check the first item, since we always start siloing from index 0.
// The first item should either be empty or not siloed (contract_address != 0).
let note_hash = self.previous_kernel.end.note_hashes[0];
Expand All @@ -184,16 +188,31 @@ impl<let NH_RR_PENDING: u32, let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, le

// Check siloing.
let kept_note_hashes = self.hints.kept_note_hashes;
let siloed_note_hashes = self.output.end.note_hashes;
let output_note_hashes = self.output.end.note_hashes;
let sorted_indexes = self.hints.sorted_note_hash_indexes;
let tx_hash = self.output.end.nullifiers[0].value(); // First nullifier is tx hash.
let first_nullifier = self.output.end.nullifiers[0].value();
let is_private_only = self.output.is_private_only;
let min_revertible_side_effect_counter = self.output.min_revertible_side_effect_counter;

for i in 0..kept_note_hashes.len() {
if i < self.note_hash_siloing_amount {
let note_hash = kept_note_hashes[i];
let sorted_index = sorted_indexes[i];
let siloed_note_hash = siloed_note_hashes[sorted_index];
let siloed_value = silo_note_hash(note_hash, tx_hash, sorted_index);
assert_eq(siloed_note_hash.value(), siloed_value, "incorrect siloed note hashes");
let output_note_hash = output_note_hashes[sorted_index];
let note_hash = kept_note_hashes[i];
let siloed_note_hash = silo_note_hash(note_hash);
let siloed_unique_note_hash = compute_unique_siloed_note_hash(
siloed_note_hash,
first_nullifier,
sorted_index,
);
// We don't silo with nonce revertible note hashes, since we don't know their final position in the tx
let expected_value = if is_private_only
| (note_hash.counter() < min_revertible_side_effect_counter) {
siloed_unique_note_hash
} else {
siloed_note_hash
};
assert_eq(output_note_hash.value(), expected_value, "incorrect siloed note hashes");
} else {
// Don't have to check empty items here.
// assert_sorted_transformed_value_array_capped_size ensures that there are the same amount of empty items padded in kept_note_hashes and in self.output.end.note_hashes.
Expand All @@ -203,7 +222,7 @@ impl<let NH_RR_PENDING: u32, let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, le
// Check ordering.
assert_sorted_transformed_value_array_capped_size(
kept_note_hashes,
siloed_note_hashes,
output_note_hashes,
|prev: ScopedNoteHash, out: ScopedNoteHash| {
out.contract_address.is_zero() & (out.counter() == prev.counter())
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct PrivateKernelInitCircuitPrivateInputs {
vk_tree_root: Field,
protocol_contract_tree_root: Field,
private_call: PrivateCallData,
is_private_only: bool,
}

impl PrivateKernelInitCircuitPrivateInputs {
Expand All @@ -27,12 +28,14 @@ impl PrivateKernelInitCircuitPrivateInputs {
protocol_contract_tree_root: Field,
private_call: PrivateCallDataWithoutPublicInputs,
app_public_inputs: PrivateCircuitPublicInputs,
is_private_only: bool,
) -> Self {
Self {
tx_request,
vk_tree_root,
protocol_contract_tree_root,
private_call: private_call.to_private_call_data(app_public_inputs),
is_private_only,
}
}

Expand All @@ -43,6 +46,7 @@ impl PrivateKernelInitCircuitPrivateInputs {
private_call_public_inputs,
self.vk_tree_root,
self.protocol_contract_tree_root,
self.is_private_only,
)
.with_private_call(private_call_public_inputs)
.finish()
Expand Down Expand Up @@ -73,6 +77,7 @@ impl PrivateKernelInitCircuitPrivateInputs {
private_call_data_validator.array_lengths,
self.vk_tree_root,
self.protocol_contract_tree_root,
self.is_private_only,
);
}
output
Expand Down Expand Up @@ -106,6 +111,7 @@ mod tests {
private_call,
vk_tree_root: FixtureBuilder::vk_tree_root(),
protocol_contract_tree_root: 0,
is_private_only: false,
}
.execute()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ mod tests {
private_log::PrivateLogData, side_effect::scoped::Scoped,
},
address::AztecAddress,
hash::{silo_note_hash, silo_nullifier, silo_private_log},
hash::{compute_unique_siloed_note_hash, silo_note_hash, silo_nullifier, silo_private_log},
point::Point,
tests::{fixture_builder::FixtureBuilder, utils::{assert_array_eq, swap_items}},
traits::is_empty_array,
Expand Down Expand Up @@ -245,14 +245,27 @@ mod tests {
}

pub fn compute_output_note_hashes<let N: u32>(
self,
self: Self,
note_hashes: [ScopedNoteHash; N],
) -> [ScopedNoteHash; N] {
// First nullifier is tx hash.
let tx_hash = self.previous_kernel.nullifiers.get_unchecked(0).value();
let first_nullifier = self.previous_kernel.nullifiers.get_unchecked(0).value();
let is_private_only = self.previous_kernel.is_private_only;
let min_revertible_side_effect_counter =
self.previous_kernel.min_revertible_side_effect_counter;

let mut output = note_hashes;
for i in 0..N {
output[i].note_hash.value = silo_note_hash(note_hashes[i], tx_hash, i);
let note_hash = note_hashes[i];
let siloed_note_hash = silo_note_hash(note_hash);
let unique_note_hash =
compute_unique_siloed_note_hash(siloed_note_hash, first_nullifier, i);
// We don't silo with nonce revertible note hashes, since we don't know their final position in the tx
output[i].note_hash.value = if is_private_only
| (note_hash.counter() < min_revertible_side_effect_counter) {
unique_note_hash
} else {
siloed_note_hash
};
output[i].contract_address = AztecAddress::zero();
}
output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ mod tests {
let mut previous_kernel = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_INNER_INDEX);
previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1_000_000, 1_000_000);
previous_kernel.set_first_nullifier();
previous_kernel.is_private_only = true;

PrivateKernelTailInputsBuilder { previous_kernel }
}
Expand Down
Loading

0 comments on commit d327da1

Please sign in to comment.