Private Kernel Circuit - Tail
Requirements
The tail circuit abstains from processing individual private function calls. Instead, it incorporates the outcomes of a private kernel circuit and conducts additional processing essential for generating the final public inputs suitable for submission to the transaction pool, subsequently undergoing processing by Sequencers and Provers. The final public inputs must safeguard against revealing any private information unnecessary for the execution of public kernel circuits and rollup circuits.
Verification of the Previous Iteration
Verifying the previous kernel proof.
It verifies that the previous iteration was executed successfully with the given proof data, verification key, and public inputs, sourced from private_inputs.previous_kernel.
The preceding proof can be:
An inner iteration may be omitted when there's only a single private function call for the transaction. And a reset iteration can be skipped if there are no read requests and transient notes in the public inputs from the last iteration.
Ensuring the previous iteration is the last.
It checks the data within private_inputs.previous_kernel.public_inputs.transient_accumulated_data to ensure that no further private kernel iteration is needed.
-
The following must be empty to ensure all the private function calls are processed:
private_call_request_stack
-
The following must be empty to ensure a comprehensive final reset:
note_hash_read_requestsnullifier_read_requestskey_validation_request_contexts- The
nullifier_counterassociated with each note hash innote_hash_contexts. - The
note_hash_counterassociated with each nullifier innullifier_contexts.
A reset iteration should ideally precede this step. Although it doesn't have to be executed immediately before the tail circuit, as long as it effectively clears the specified values.
Processing Final Outputs
Siloing values.
Siloing a value with the address of the contract generating the value ensures that data produced by a contract is accurately attributed to the correct contract and cannot be misconstrued as data created in a different contract. This circuit guarantees the following siloed values:
-
Silo
nullifiers:For each
nullifierat indexi > 0in thenullifier_contextswithinprivate_inputs, ifnullifier.value != 0:nullifier_contexts[i].value = hash(nullifier.contract_address, nullifier.value)This process does not apply to
nullifier_contexts[0], which is the hash of the transaction request created by the initial private kernel circuit. -
Silo
note_hashes:For each
note_hashat indexiin thenote_hash_contextswithinprivate_inputs, ifnote_hash.value != 0:note_hash_contexts[i].value = hash(note_nonce, siloed_hash)Where:
note_nonce = hash(first_nullifier, index)first_nullifier = nullifier_contexts[0].value.index = note_hash_hints[i], which is the index of the same note hash withinpublic_inputs.note_hashes. Wherenote_hash_hintsis provided as hints viaprivate_inputs.
siloed_hash = hash(note_hash.contract_address, note_hash.value)
Siloing with a
note_nonceguarantees that each final note hash is a unique value in the note hash tree. -
Silo
l2_to_l1_messages:For each
l2_to_l1_messageat indexiinl2_to_l1_message_contextswithin [private_inputs], ifl2_to_l1_message.value != 0:l2_to_l1_message_contexts[i].value = hash(l2_to_l1_message.contract_address, version_id, l2_to_l1_message.portal_contract_address, chain_id, l2_to_l1_message.value)Where
version_idandchain_idare defined inpublic_inputs.constant_data.tx_context. -
Silo
unencrypted_log_hashes:For each
log_hashat indexiin theunencrypted_log_hash_contextswithinprivate_inputs, iflog_hash.hash != 0:unencrypted_log_hash_contexts[i].value = hash(log_hash.hash, log_hash.contract_address) -
Silo
encrypted_log_hashes:For each
log_hashat indexiin theencrypted_log_hash_contextswithinprivate_inputs, iflog_hash.hash != 0:encrypted_log_hash_contexts[i].value = hash(log_hash.hash, contract_address_tag)Where
contract_address_tag = hash(log_hash.contract_address, log_hash.randomness)
Verifying and splitting ordered data.
The initial and inner kernel iterations may produce values in an unordered state due to the serial nature of the kernel, contrasting with the stack-based nature of code execution.
This circuit ensures the correct ordering of the following:
note_hashesnullifiersl2_to_l1_messagesunencrypted_log_hashesencrypted_log_hashesencrypted_note_preimage_hashespublic_call_requests
In addition, the circuit split the ordered data into non_revertible_accumulated_data and revertible_accumulated_data using min_revertible_side_effect_counter.
-
Verify ordered
public_call_requests:Initialize
num_non_revertibleandnum_revertibleto0.For each
requestat indexiin the unorderedpublic_call_request_contextswithinprivate_inputs.previous_kernel.public_inputs.transient_accumulated_data:- Find its associated
mapped_requestinpublic_call_requests[public_call_request_hints[i]]withinpublic_inputs.- If
request.counter < min_revertible_side_effect_counter:- The
public_call_requestsis innon_revertible_accumulated_data. num_added = num_non_revertible.
- The
- If
request.counter >= min_revertible_side_effect_counter:- The
public_call_requestsis inrevertible_accumulated_data. num_added = num_revertible.
- The
- If
- If
request.call_stack_item_hash != 0, verify that:request == mapped_request- If
num_added > 0, verify that:public_call_requests[num_added].counter < public_call_requests[num_added - 1].counter
- Increment
num_addedby1:num_(non_)revertible += 1
- Else:
- All the subsequent requests (
index >= i) inpublic_call_request_contextsmust be empty. - All the subsequent requests (
index >= num_non_revertible) innon_revertible_accumulated_data.public_call_requestsmust be empty. - All the subsequent requests (
index >= num_revertible) inrevertible_accumulated_data.public_call_requestsmust be empty.
- All the subsequent requests (
Note that requests in
public_call_requestsmust be arranged in descending order to ensure the calls are executed in chronological order. - Find its associated
-
Verify the rest of the ordered arrays:
Initialize
num_non_revertibleandnum_revertibleto0.For each
note_hash_contextat indexiin the unorderednote_hash_contextswithinprivate_inputs.previous_kernel.public_inputs.transient_accumulated_data:- Find its associated
note_hashinnote_hashes[note_hash_hints[i].index]withinpublic_inputs.- If
note_hash_context.counter < min_revertible_side_effect_counter:- The
note_hashesis innon_revertible_accumulated_data. num_added = num_non_revertible.
- The
- If
note_hash_context.counter >= min_revertible_side_effect_counter:- The
note_hashesis inrevertible_accumulated_data. num_added = num_revertible.
- The
- If
- If
note_hash_context.value != 0, verify that:note_hash == note_hash_context.valuenote_hash_hints[note_hash_hints[i].index].counter_(non_)revertible == note_hash_context.counter- If
num_added > 0, verify that:note_hash_hints[num_added].counter_(non_)revertible > note_hash_hints[num_added - 1].counter_(non_)revertible
- Increment
num_addedby1:num_(non_)revertible += 1
- Else:
- All the subsequent elements (index
>= i) innote_hash_contextsmust be empty. - All the subsequent elements (index
>= num_non_revertible) innon_revertible_accumulated_data.note_hashesmust be empty. - All the subsequent elements (index
>= num_revertible) inrevertible_accumulated_data.note_hashesmust be empty.
- All the subsequent elements (index
Repeat the same process for
nullifiers,l2_to_l1_messages,unencrypted_log_hashes,encrypted_log_hashes, andencrypted_note_preimage_hashes, where:- Ordered
nullifiersandl2_to_l1_messagesare withinpublic_inputs. ordered_unencrypted_log_hashes_(non_)revertible,ordered_encrypted_log_hashes_(non_)revertible, andordered_encrypted_note_preimage_hashes_(non_)revertibleare provided ashintsthroughprivate_inputs.
- Find its associated
While ordering could occur gradually in each kernel iteration, the implementation is much simpler and typically more efficient to be done once in the tail circuit.
Recalibrating counters.
While the counter of a public_call_request is initially assigned in the private function circuit to ensure proper ordering within the transaction, it should be modified in this step. As using counter values obtained from private function circuits may leak information.
The requests in the public_call_requests within public_inputs have been sorted in descending order in the previous step. This circuit recalibrates their counters through the following steps:
- The
counterof the last non-empty request is set to1. - The
counters of the other non-empty requests are continuous values in descending order:public_call_requests[i].counter = public_call_requests[i + 1].counter + 1
It's crucial for the
counterof the last request to be1, as it's assumed in the tail public kernel circuit that no storage writes have a counter1.
Validating Public Inputs
Verifying the (non-)revertible accumulated data.
-
The following must align with the results after ordering, as verified in a previous step:
note_hashesnullifiersl2_to_l1_messages
-
The
public_call_requestsmust adhere to a specific order with recalibrated counters, as verified in the previous steps. -
The hashes and lengths for all logs are accumulated as follows:
For each non-empty
log_hashat indexiinordered_unencrypted_log_hashes_(non_)revertible, which is provided as hints, and the ordering was verified against the siloed hashes in previous steps:accumulated_logs_hash = hash(accumulated_logs_hash, log_hash.hash)- If
i == 0:accumulated_logs_hash = log_hash.hash
- If
accumulated_logs_length += log_hash.length
Check the values in the
public_inputsare correct:unencrypted_logs_hash == accumulated_logs_hashunencrypted_log_preimages_length == accumulated_logs_length
Repeat the same process for
encrypted_logs_hashesandencrypted_note_preimages_hashes.
Verifying the transient accumulated data.
It ensures that all data in the transient_accumulated_data within public_inputs is empty.
Verifying other data.
This section follows the same process as outlined in the inner private kernel circuit.
In addition, it checks that the following are empty:
old_public_data_tree_snapshotnew_public_data_tree_snapshot
PrivateInputs
PreviousKernel
The format aligns with the PreviousKernel of the inner private kernel circuit.
Hints
Data that aids in the verifications carried out in this circuit:
| Field | Type | Description |
|---|---|---|
note_hash_hints | [OrderHint; MAX_NOTE_HASHES_PER_TX] | Hints for ordering note_hash_contexts. |
nullifier_hints | [OrderHint; MAX_NULLIFIERS_PER_TX] | Hints for ordering nullifier_contexts. |
public_call_request_hints | [field; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX] | Indices of ordered public_call_request_contexts. |
unencrypted_log_hash_hints | [OrderHint; MAX_UNENCRYPTED_LOG_HASHES_PER_TX] | Hints for ordering unencrypted_log_hash_contexts. |
ordered_unencrypted_log_hashes_revertible | [field; MAX_UNENCRYPTED_LOG_HASHES_PER_TX] | Ordered revertible unencrypted_log_hashes. |
ordered_unencrypted_log_hashes_non_revertible | [field; MAX_UNENCRYPTED_LOG_HASHES_PER_TX] | Ordered non-revertible unencrypted_log_hashes. |
encrypted_log_hash_hints | [OrderHint; MAX_ENCRYPTED_LOG_HASHES_PER_TX] | Hints for ordering encrypted_log_hash_contexts. |
ordered_encrypted_log_hashes_revertible | [field; MAX_ENCRYPTED_LOG_HASHES_PER_TX] | Ordered revertible encrypted_log_hashes. |
ordered_encrypted_log_hashes_non_revertible | [field; MAX_ENCRYPTED_LOG_HASHES_PER_TX] | Ordered non-revertible encrypted_log_hashes. |
encrypted_note_preimage_hints | [OrderHint; MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX] | Hints for ordering encrypted_note_preimage_hash_contexts. |
ordered_encrypted_note_preimage_hashes_revertible | [field; MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX] | Ordered revertible encrypted_note_preimage_hashes. |
ordered_encrypted_note_preimage_hashes_non_revertible | [field; MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX] | Ordered non-revertible encrypted_note_preimage_hashes. |
OrderHint
| Field | Type | Description |
|---|---|---|
index | field | Index of the mapped element in the ordered array. |
counter_revertible | u32 | Counter of the element at index i in the revertible ordered array. |
counter_non_revertible | u32 | Counter of the element at index i in the non-revertible ordered array. |
PublicInputs
The format aligns with the Public Inputs of the tail public kernel circuit.