Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move siloing to reset #7871

Merged
merged 8 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading