Skip to content

Commit

Permalink
refactor: move siloing to reset (#7871)
Browse files Browse the repository at this point in the history
Please read [contributing guidelines](CONTRIBUTING.md) and remove this
line.
  • Loading branch information
LeilaWang authored Aug 13, 2024
1 parent 5e25cd6 commit 014b5f0
Show file tree
Hide file tree
Showing 59 changed files with 2,083 additions and 1,367 deletions.
9 changes: 5 additions & 4 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ library Constants {
uint256 internal constant PRIVATE_KERNEL_INIT_INDEX = 0;
uint256 internal constant PRIVATE_KERNEL_INNER_INDEX = 1;
uint256 internal constant PRIVATE_KERNEL_RESET_FULL_INDEX = 2;
uint256 internal constant PRIVATE_KERNEL_RESET_BIG_INDEX = 3;
uint256 internal constant PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 4;
uint256 internal constant PRIVATE_KERNEL_RESET_SMALL_INDEX = 5;
uint256 internal constant PRIVATE_KERNEL_RESET_TINY_INDEX = 6;
uint256 internal constant PRIVATE_KERNEL_RESET_FULL_INNER_INDEX = 3;
uint256 internal constant PRIVATE_KERNEL_RESET_BIG_INDEX = 4;
uint256 internal constant PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 5;
uint256 internal constant PRIVATE_KERNEL_RESET_SMALL_INDEX = 6;
uint256 internal constant PRIVATE_KERNEL_RESET_TINY_INDEX = 7;
uint256 internal constant PRIVATE_KERNEL_TAIL_INDEX = 10;
uint256 internal constant PRIVATE_KERNEL_TAIL_TO_PUBLIC_INDEX = 11;
uint256 internal constant EMPTY_NESTED_INDEX = 12;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod private_call_data_validator;
mod private_kernel_circuit_output_validator;
mod private_kernel_circuit_public_inputs_composer;
mod reset_output_composer;
mod reset_output_validator;
mod tail_output_composer;
mod tail_output_validator;
mod tail_to_public_output_composer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ mod previous_kernel_validator_hints;

use crate::components::previous_kernel_validator::previous_kernel_validator_hints::{generate_previous_kernel_validator_hints, PreviousKernelValidatorHints};
use dep::types::{
abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, utils::arrays::array_length,
traits::is_empty
abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::ScopedEncryptedLogHash},
address::AztecAddress, traits::is_empty, utils::arrays::array_length
};

struct PreviousKernelValidator {
Expand All @@ -30,18 +30,20 @@ impl PreviousKernelValidator {
fn validate_common(self) {
self.validate_empty_private_call_stack();
self.verify_empty_validation_requests();
self.verify_sorted_siloed_values();
self.validate_no_transient_data();
}

fn validate_empty_private_call_stack(self) {
assert_eq(
array_length(self.previous_kernel.end.private_call_stack), 0, "Private call stack must be empty when executing the tail circuit"
// Only need to check the first item as the kernel circuits always append items to the arrays properly.
assert(
is_empty(self.previous_kernel.end.private_call_stack[0]), "Private call stack must be empty when executing the tail circuit"
);
}

fn validate_empty_data(self) {
assert_eq(
array_length(self.previous_kernel.end.public_call_requests), 0, "Public call stack must be empty when executing the tail circuit"
assert(
is_empty(self.previous_kernel.end.public_call_requests[0]), "Public call stack must be empty when executing the tail circuit"
);
assert(
is_empty(self.previous_kernel.public_teardown_call_request), "Public teardown call request must be empty when executing the tail circuit"
Expand All @@ -57,9 +59,9 @@ impl PreviousKernelValidator {
}

fn validate_non_empty_data(self) {
let len = array_length(self.previous_kernel.end.public_call_requests);
assert(
(len != 0) | !is_empty(self.previous_kernel.public_teardown_call_request), "Must have public calls when exporting public kernel data from the tail circuit"
!is_empty(self.previous_kernel.end.public_call_requests[0])
| !is_empty(self.previous_kernel.public_teardown_call_request), "Must have public calls when exporting public kernel data from the tail circuit"
);

assert(
Expand All @@ -74,31 +76,62 @@ impl PreviousKernelValidator {
}

fn verify_empty_validation_requests(self) {
assert_eq(
array_length(self.previous_kernel.validation_requests.note_hash_read_requests), 0, "Non empty note hash read requests"
assert(
is_empty(self.previous_kernel.validation_requests.note_hash_read_requests[0]), "Non empty note hash read requests"
);
assert_eq(
array_length(self.previous_kernel.validation_requests.nullifier_read_requests), 0, "Non empty nullifier read requests"
assert(
is_empty(self.previous_kernel.validation_requests.nullifier_read_requests[0]), "Non empty nullifier read requests"
);
assert(
is_empty(self.previous_kernel.validation_requests.scoped_key_validation_requests_and_generators[0]), "Non empty key validation requests"
);
}

fn verify_sorted_siloed_values(self) {
// Check that the data are already siloed and/or sorted in the reset circuit.
// Any unprocessed data added after the last reset with siloing was called should be caught here.

let num_note_hashes = array_length(self.previous_kernel.end.note_hashes);
if num_note_hashes != 0 {
let note_hash = self.previous_kernel.end.note_hashes[num_note_hashes - 1];
assert_eq(
note_hash.contract_address, AztecAddress::zero(), "note hashes have not been siloed in a reset"
);
}

let num_nullifiers = array_length(self.previous_kernel.end.nullifiers);
let nullifier = self.previous_kernel.end.nullifiers[num_nullifiers - 1]; // - 1 without checking because there's at least 1 nullifier.
assert_eq(
array_length(self.previous_kernel.validation_requests.scoped_key_validation_requests_and_generators), 0, "Non empty key validation requests"
nullifier.contract_address, AztecAddress::zero(), "nullifiers have not been siloed in a reset"
);

// Note logs are not siloed, but they are sorted and their note_hash_counter should've been set to 0 in the reset circuit.
let num_note_logs = array_length(self.previous_kernel.end.note_encrypted_logs_hashes);
if num_note_logs != 0 {
let note_log = self.previous_kernel.end.note_encrypted_logs_hashes[num_note_logs - 1];
assert_eq(note_log.note_hash_counter, 0, "note logs have not been sorted in a reset");
}

// We need to check the entire array because randomness can be 0 for encrypted logs if the app wants to reveal the actual contract address.
assert(
self.previous_kernel.end.encrypted_logs_hashes.all(|h: ScopedEncryptedLogHash| h.log_hash.randomness == 0), "encrypted logs have not been siloed in a reset"
);
}

fn validate_no_transient_data(self) {
let nullifiers = self.previous_kernel.end.nullifiers;
let inner_note_hashes = self.previous_kernel.end.note_hashes;
let siloed_note_hashes = self.hints.siloed_note_hashes;
let note_hashes = self.previous_kernel.end.note_hashes;
let note_hash_indexes_for_nullifiers = self.hints.note_hash_indexes_for_nullifiers;
for i in 0..nullifiers.len() {
let nullified_note_hash = nullifiers[i].nullifier.note_hash;
let nullifier = nullifiers[i];
let nullified_note_hash = nullifier.nullifier.note_hash;
if nullified_note_hash != 0 {
let note_hash_index = note_hash_indexes_for_nullifiers[i];
let note_hash = note_hashes[note_hash_indexes_for_nullifiers[i]];
assert_eq(
nullified_note_hash, siloed_note_hashes[note_hash_index], "Hinted siloed note hash does not match nullified note hash"
note_hash.value(), nullified_note_hash, "Hinted siloed note hash does not match nullified note hash"
);
assert(
inner_note_hashes[note_hash_index].counter() < nullifiers[i].counter(), "Cannot link a note hash emitted after a nullifier"
note_hash.counter() < nullifier.counter(), "Cannot link a note hash emitted after a nullifier"
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
use dep::types::{
abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs,
constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, hash::silo_note_hash,
utils::arrays::{find_index, sort_get_order_hints_asc}
abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, note_hash::ScopedNoteHash},
constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, utils::arrays::find_index
};

struct PreviousKernelValidatorHints {
siloed_note_hashes: [Field; MAX_NOTE_HASHES_PER_TX],
note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX],
}

unconstrained pub fn generate_previous_kernel_validator_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> PreviousKernelValidatorHints {
let mut siloed_note_hashes = [0; MAX_NOTE_HASHES_PER_TX];
let unsilod_note_hashes = previous_kernel.end.note_hashes;
let sorted_note_hash_hints = sort_get_order_hints_asc(unsilod_note_hashes);
let tx_hash = previous_kernel.end.nullifiers[0].value(); // First nullifier is tx hash.
for i in 0..unsilod_note_hashes.len() {
siloed_note_hashes[i] = silo_note_hash(unsilod_note_hashes[i], tx_hash, sorted_note_hash_hints[i].sorted_index);
}

let mut note_hash_indexes_for_nullifiers = [0; MAX_NULLIFIERS_PER_TX];
let note_hashes = previous_kernel.end.note_hashes;
let nullifiers = previous_kernel.end.nullifiers;
for i in 0..nullifiers.len() {
let nullified_note_hash = nullifiers[i].nullifier.note_hash;
let note_hash_index = find_index(siloed_note_hashes, |n: Field| n == nullified_note_hash);
let note_hash_index = find_index(
note_hashes,
|n: ScopedNoteHash| n.value() == nullified_note_hash
);
if (nullified_note_hash != 0) & (note_hash_index != MAX_NOTE_HASHES_PER_TX) {
note_hash_indexes_for_nullifiers[i] = note_hash_index;
}
}

PreviousKernelValidatorHints { siloed_note_hashes, note_hash_indexes_for_nullifiers }
PreviousKernelValidatorHints { note_hash_indexes_for_nullifiers }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use dep::types::{
MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTE_HASHES_PER_TX,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL
},
hash::{silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, traits::is_empty,
transaction::tx_request::TxRequest,
traits::is_empty, transaction::tx_request::TxRequest,
utils::arrays::{array_length, array_to_bounded_vec, sort_by_counters_asc, sort_by_counters_desc}
};

Expand Down Expand Up @@ -100,9 +99,11 @@ impl PrivateKernelCircuitPublicInputsComposer {
*self
}

pub fn sort_and_silo(&mut self) {
self.sort_ordered_values();
self.silo_scoped_values();
pub fn sort_ordered_values(&mut self) {
// Note hashes, nullifiers, note_encrypted_logs_hashes, and encrypted_logs_hashes are sorted in the reset circuit.
self.public_inputs.end.l2_to_l1_msgs.storage = sort_by_counters_asc(self.public_inputs.end.l2_to_l1_msgs.storage);
self.public_inputs.end.unencrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.unencrypted_logs_hashes.storage);
self.public_inputs.end.public_call_requests.storage = sort_by_counters_desc(self.public_inputs.end.public_call_requests.storage);
}

pub fn finish(self) -> PrivateKernelCircuitPublicInputs {
Expand Down Expand Up @@ -262,46 +263,4 @@ impl PrivateKernelCircuitPublicInputsComposer {
self.public_inputs.fee_payer = source.storage_contract_address;
}
}

fn sort_ordered_values(&mut self) {
self.public_inputs.end.note_hashes.storage = sort_by_counters_asc(self.public_inputs.end.note_hashes.storage);
self.public_inputs.end.nullifiers.storage = sort_by_counters_asc(self.public_inputs.end.nullifiers.storage);
self.public_inputs.end.l2_to_l1_msgs.storage = sort_by_counters_asc(self.public_inputs.end.l2_to_l1_msgs.storage);
self.public_inputs.end.note_encrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.note_encrypted_logs_hashes.storage);
self.public_inputs.end.encrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.encrypted_logs_hashes.storage);
self.public_inputs.end.unencrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.unencrypted_logs_hashes.storage);
self.public_inputs.end.public_call_requests.storage = sort_by_counters_desc(self.public_inputs.end.public_call_requests.storage);
}

fn silo_scoped_values(&mut self) {
self.silo_note_hashes();
self.silo_nullifiers();
self.mask_encrypted_logs();
}

fn silo_note_hashes(&mut self) {
let first_nullifier = self.public_inputs.end.nullifiers.get_unchecked(0).value();
let note_hashes = self.public_inputs.end.note_hashes.storage;
for i in 0..note_hashes.len() {
self.public_inputs.end.note_hashes.storage[i].note_hash.value = silo_note_hash(
note_hashes[i],
first_nullifier,
i
);
}
}

fn silo_nullifiers(&mut self) {
let nullifiers = self.public_inputs.end.nullifiers.storage;
for i in 0..nullifiers.len() {
self.public_inputs.end.nullifiers.storage[i].nullifier.value = silo_nullifier(nullifiers[i]);
}
}

fn mask_encrypted_logs(&mut self) {
let logs = self.public_inputs.end.encrypted_logs_hashes.storage;
for i in 0..logs.len() {
self.public_inputs.end.encrypted_logs_hashes.storage[i].contract_address = mask_encrypted_log_hash(logs[i]);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,117 @@
mod squash_transient_data;
mod reset_output_hints;

use crate::components::reset_output_composer::squash_transient_data::squash_transient_data;
use crate::components::reset_output_composer::{reset_output_hints::{generate_reset_output_hints, ResetOutputHints}};
use dep::types::{
abis::{
kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::NoteLogHash,
note_hash::ScopedNoteHash, nullifier::ScopedNullifier
kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs,
log_hash::{NoteLogHash, ScopedEncryptedLogHash}, note_hash::ScopedNoteHash,
nullifier::ScopedNullifier
},
constants::{MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}
address::AztecAddress,
constants::{
MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX,
MAX_NULLIFIERS_PER_TX
},
hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, utils::arrays::sort_by_counters_asc
};

struct PrivateKernelResetOutputs {
note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX],
nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX],
note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX],
encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX],
}

struct ResetOutputComposer {
output: PrivateKernelResetOutputs,
previous_kernel: PrivateKernelCircuitPublicInputs,
note_hash_siloing_amount: u32,
nullifier_siloing_amount: u32,
encrypted_log_siloing_amount: u32,
hints: ResetOutputHints,
}

impl ResetOutputComposer {
pub fn new(
previous_kernel: PrivateKernelCircuitPublicInputs,
transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX],
transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX]
transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX],
note_hash_siloing_amount: u32,
nullifier_siloing_amount: u32,
encrypted_log_siloing_amount: u32
) -> Self {
let (note_hashes, nullifiers, note_encrypted_log_hashes) = squash_transient_data(
previous_kernel.end.note_hashes,
previous_kernel.end.nullifiers,
previous_kernel.end.note_encrypted_logs_hashes,
let hints = generate_reset_output_hints(
previous_kernel,
transient_nullifier_indexes_for_note_hashes,
transient_note_hash_indexes_for_nullifiers
);
let output = PrivateKernelResetOutputs { note_hashes, nullifiers, note_encrypted_log_hashes };
ResetOutputComposer { output }
ResetOutputComposer { previous_kernel, note_hash_siloing_amount, nullifier_siloing_amount, encrypted_log_siloing_amount, hints }
}

pub fn finish(self) -> PrivateKernelResetOutputs {
self.output
let note_hashes = if self.note_hash_siloing_amount == 0 {
self.hints.kept_note_hashes
} else {
self.get_sorted_siloed_note_hashes()
};

let nullifiers = if self.nullifier_siloing_amount == 0 {
self.hints.kept_nullifiers
} else {
self.get_sorted_siloed_nullifiers()
};

let note_encrypted_log_hashes = if self.note_hash_siloing_amount == 0 {
self.hints.kept_note_encrypted_log_hashes
} else {
self.get_sorted_note_encrypted_log_hashes()
};

let encrypted_log_hashes = if self.encrypted_log_siloing_amount == 0 {
self.previous_kernel.end.encrypted_logs_hashes
} else {
self.get_sorted_masked_encrypted_log_hashes()
};

PrivateKernelResetOutputs { note_hashes, nullifiers, note_encrypted_log_hashes, encrypted_log_hashes }
}

fn get_sorted_siloed_note_hashes(self) -> [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX] {
let mut note_hashes = sort_by_counters_asc(self.hints.kept_note_hashes);
let first_nullifier = self.previous_kernel.end.nullifiers[0].value();
for i in 0..note_hashes.len() {
note_hashes[i].note_hash.value = silo_note_hash(
note_hashes[i],
first_nullifier,
i
);
note_hashes[i].contract_address = AztecAddress::zero();
}
note_hashes
}

fn get_sorted_siloed_nullifiers(self) -> [ScopedNullifier; MAX_NULLIFIERS_PER_TX] {
let mut nullifiers = sort_by_counters_asc(self.hints.kept_nullifiers);
for i in 0..nullifiers.len() {
nullifiers[i].nullifier.value = silo_nullifier(nullifiers[i]);
nullifiers[i].contract_address = AztecAddress::zero();
}
nullifiers
}

fn get_sorted_note_encrypted_log_hashes(self) -> [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX] {
let mut log_hashes = sort_by_counters_asc(self.hints.kept_note_encrypted_log_hashes);
for i in 0..log_hashes.len() {
log_hashes[i].note_hash_counter = 0;
}
log_hashes
}

fn get_sorted_masked_encrypted_log_hashes(self) -> [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX] {
let mut log_hashes = sort_by_counters_asc(self.previous_kernel.end.encrypted_logs_hashes);
for i in 0..log_hashes.len() {
log_hashes[i].contract_address = mask_encrypted_log_hash(log_hashes[i]);
log_hashes[i].log_hash.randomness = 0;
}
log_hashes
}
}
Loading

0 comments on commit 014b5f0

Please sign in to comment.