Private Kernel Circuit - Reset
Requirements
A reset circuit is designed to abstain from processing individual private function calls. Instead, it injects the outcomes of an initial, inner, or another reset private kernel circuit, scrutinizes the public inputs, and clears the verifiable data within its scope. A reset circuit can be executed either preceding the tail private kernel circuit, or as a means to "reset" public inputs at any point between two private kernels, allowing data to accumulate seamlessly in subsequent iterations.
There are 3 variations of reset circuits:
- Read Request Reset Private Kernel Circuit.
- Parent Secret Key Validation Request Reset Private Kernel Circuit.
- Transient Note Reset Private Kernel Circuit.
The incorporation of these circuits not only enhances the modularity and repeatability of the "reset" process but also diminishes the overall workload. Rather than conducting resource-intensive computations such as membership checks in each iteration, these tasks are only performed as necessary within the reset circuits.
Read Request Reset Private Kernel Circuit.
This reset circuit conducts verification on some or all accumulated read requests and subsequently removes them from the transient_accumulated_data
within the public_inputs
of the previous_kernel
.
Depending on the value specified in hints
.reset_type
, it can target different read requests for resetting:
- For
reset_type == note_hash
:target_read_requests = note_hash_read_requests
- For
reset_type == nullifier
:target_read_requests = nullifier_read_requests
A read request can pertain to one of two types of values:
- A settled value: generated in a prior successful transaction and included in the tree.
- A pending value: created in the current transaction, not yet part of the tree.
To clear read requests for settled values, the circuit performs membership checks for the target read requests using the hints provided via
private_inputs
.For each
persistent_read_index
at indexi
inhints.persistent_read_indices
:- If the
persistent_read_index
equals the length of thetarget_read_requests
array, there is no read request to be verified. Skip the rest. - Locate the
read_request
using the index:read_request = target_read_requests[persistent_read_index]
- Perform a membership check on the value being read. Where:
- The leaf corresponds to the value:
read_request.value
- The index and sibling path are in:
hints.read_request_membership_witnesses[i]
. - The root is sourced from the block_header within
public_inputs
.constant_data
:- For note hash:
note_hash_tree_root
- For nullifier:
nullifier_tree_root
- For note hash:
- The leaf corresponds to the value:
Following the above process, at most
N
read requests will be cleared, whereN
is the length of thepersistent_read_indices
array. It's worth noting that there can be multiple versions of this reset circuit, each with a different value ofN
.- If the
To clear read requests for pending values, the circuit ensures that the values were created before the corresponding read operations, utilizing the hints provided via
private_inputs
.For each
transient_read_index
at indexi
inhints.transient_read_indices
:- If the
transient_read_index
equals the length of thetarget_read_requests
array, there is no read request to be verified. Skip the rest. - Locate the
read_request
using the index:read_request = target_read_requests[transient_read_index]
- Locate the
target
being read using the indexhints.pending_value_indices[i]
:- For note hash:
target = note_hash_contexts[index]
- For nullifier:
target = nullifier_contexts[index]
- For note hash:
- Verify the following:
read_request.value == target.value
read_request.contract_address == target.contract_address
read_request.counter > target.counter
- When resetting a note hash, verify that the target note hash is not nullified before the read happens:
(target.nullifier_counter > read_request.counter) | (target.nullifier_counter == 0)
Given that a reset circuit can execute between two private kernel circuits, there's a possibility that the value being read is emitted in a nested execution and hasn't been included in the
public_inputs
. In such cases, the read request cannot be verified in the current reset circuit and must be processed in another reset circuit after the value has been aggregated to thepublic_inputs
.- If the
This circuit then ensures that the read requests that haven't been verified should remain in the transient_accumulated_data within its
public_inputs
.For each
read_request
at indexi
in thetarget_read_requests
, find itsstatus
athints.read_request_statuses[i]
. Verify the following:- If
status.state == persistent
,i == persistent_read_indices[status.index]
. - If
status.state == transient
,i == transient_read_indices[status.index]
. - If
status.state == nada
,read_request == public_inputs.transient_accumulated_data.target_read_requests[status.index]
.
- If
Parent Secret Key Validation Request Reset Private Kernel Circuit.
This reset circuit validates the correct derivation of secret keys used in private functions, and subsequently removes them from the transient_accumulated_data
within the public_inputs
of the previous_kernel
.
Initialize requests_kept
to 0
.
For each request
at index i
in nullifier_key_validation_request_contexts
, locate the master_secret_key
at master_secret_keys[i]
, provided as hints through private_inputs
.
If
master_secret_key == 0
, ensure the request remain within thepublic_inputs
.:public_inputs.transient_accumulated_data.nullifier_key_validation_request_contexts[requests_kept] == request
- Increase
requests_kept
by 1:requests_kept += 1
Else:
- Verify that the public key is associated with the
master_secret_key
:request.parent_public_key == master_secret_key * G
- Verify that the secret key was correctly derived for the contract:
request.hardened_child_secret_key == hash(master_secret_key, request.contract_address)
- Verify that the public key is associated with the
Transient Note Reset Private Kernel Circuit.
In the event that a pending note is nullified within the same transaction, its note hash, nullifier, and all encrypted note preimage hashes can be removed from the public inputs. This not only avoids redundant data being broadcasted, but also frees up space for additional note hashes and nullifiers in the subsequent iterations.
Ensure that each note hash is either propagated to the
public_inputs
or nullified in the same transaction.Initialize both
notes_kept
andnotes_removed
to0
.For each
note_hash
at indexi
innote_hash_contexts
within theprivate_inputs
, find the index of its nullifier attransient_nullifier_indices[i]
, provided as hints:If
transient_nullifier_indices[i] == nullifier_contexts.len()
:- Verify that the
note_hash
remains within the transient_accumulated_data in thepublic_inputs
:note_hash == public_inputs.transient_accumulated_data.note_hash_contexts[notes_kept]
- Increment
notes_kept
by 1:notes_kept += 1
- Verify that the
Else, locate the
nullifier
atnullifier_contexts[transient_nullifier_indices[i]]
:- Verify that the nullifier is associated with the note:
nullifier.contract_address == note_hash.contract_address
nullifier.note_hash_counter == note_hash.counter
nullifier.counter == note_hash.nullifier_counter
- Increment
notes_removed
by 1:notes_removed += 1
- Ensure that an empty
note_hash
is appended to the end ofnote_hash_contexts
in thepublic_inputs
:public_inputs.transient_accumulated_data.note_hash_contexts[N - notes_removed].is_empty() == true
- Where
N
is the length ofnote_hash_contexts
.
Note that the check
nullifier.counter > note_hash.counter
is not necessary as thenullifier_counter
is assured to be greater than the counter of the note hash when propagated from either the initial or inner private kernel circuits.- Verify that the nullifier is associated with the note:
Ensure that nullifiers not associated with note hashes removed in the previous step are retained within the transient_accumulated_data in the
public_inputs
.Initialize both
nullifiers_kept
andnullifiers_removed
to0
.For each
nullifier
at indexi
in thenullifier_contexts
within theprivate_inputs
, find the index of its corresponding transient nullifier atnullifier_index_hints[i]
, provided as hints:- If
nullifier_index_hints[i] == transient_nullifier_indices.len()
:- Verify that the
nullifier
remains within thetransient_accumulated_data
in thepublic_inputs
:nullifier == public_inputs.transient_accumulated_data.nullifier_contexts[nullifiers_kept]
- Increment
nullifiers_kept
by 1:nullifiers_kept += 1
- Verify that the
- Else, compute
transient_nullifier_index
astransient_nullifier_indices[nullifier_index_hints[i]]
:- Verify that:
transient_nullifier_index == i
- Increment
nullifiers_removed
by 1:nullifiers_removed += 1
- Ensure that an empty
nullifier
is appended to the end ofnullifier_contexts
in thepublic_inputs
:public_inputs.transient_accumulated_data.nullifier_contexts[N - nullifiers_removed].is_empty() == true
- Where
N
is the length ofnullifier_contexts
.
- Verify that:
After these steps, ensure that all nullifiers associated with transient note hashes have been identified and removed:
nullifiers_removed == notes_removed
- If
Ensure that
encrypted_note_preimage_hashes
not associated with note hashes removed in the previous step are retained within the[transient_accumulated_data](/docs/circuits/private-kernel-initial#transientaccumulateddata)
in thepublic_inputs
.Initialize both
hashes_kept
andhashes_removed
to0
.For each
preimage_hash
at indexi
in theencrypted_note_preimage_hash_contexts
within theprivate_inputs
, find theindex_hint
of its corresponding hash withinpublic_inputs
atencrypted_note_preimage_hash_index_hints[i]
, provided as hints:- If
index_hint == encrypted_note_preimage_hash_contexts.len()
:- Ensure that the associated note hash is removed:
- Locate the
note_hash
atprivate_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]
. - Verify that the
preimage_hash
is associated with thenote_hash
:preimage_hash.note_hash_counter == note_hash.counter
preimage_hash.contract_address == note_hash.contract_address
- Confirm that the
note_hash
has a corresponding nullifier and has been removed in the first step of this section:transient_nullifier_indices[log_note_hash_hints[i]] != nullifier_contexts.len()
- Locate the
- Increment
hashes_removed
by 1:hashes_removed += 1
- Ensure that an empty item is appended to the end of
encrypted_note_preimage_hash_contexts
in thepublic_inputs
:encrypted_note_preimage_hash_contexts[N - hashes_removed].is_empty() == true
- Where
N
is the length ofencrypted_note_preimage_hash_contexts
.
- Ensure that the associated note hash is removed:
- Else, find the
mapped_preimage_hash
atencrypted_note_preimage_hash_contexts[index_hint]
withinpublic_inputs
:- Verify that the context is aggregated to the
public_inputs
correctly:index_hint == hashes_kept
mapped_preimage_hash == preimage_hash
- Ensure that the associated note hash is retained in the
public_inputs
:- Locate the
note_hash
atpublic_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]
. - Verify that the
preimage_hash
is associated with thenote_hash
:preimage_hash.note_hash_counter == note_hash.counter
preimage_hash.contract_address == note_hash.contract_address
- Locate the
- Increment
hashes_kept
by 1:hashes_kept += 1
- Verify that the context is aggregated to the
- If
Note that this reset process may not necessarily be applied to all transient notes at a time. In cases where a note will be read in a yet-to-be-processed nested execution, the transient note hash and its nullifier must be retained in the
public_inputs
. The reset can only occur in a later reset circuit after all associated read requests have been verified and cleared.
Common Verifications
Below are the verifications applicable to all reset circuits:
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:
- Initial private kernel proof.
- Inner private kernel proof.
- Reset private kernel proof.
Verifying the accumulated data.
It ensures that the accumulated_data
in the public_inputs
matches the accumulated_data
in private_inputs
.previous_kernel
.public_inputs
.
Verifying the transient accumulated data.
All arrays in the transient_accumulated_data
in the public_inputs
must equal their corresponding arrays in private_inputs
.previous_kernel
.public_inputs
.transient_accumulated_data
, with the exception of those modified by the reset circuits:
- Read request reset circuit (for note hashes):
note_hash_read_requests
- Read request reset circuit (for nullifiers):
nullifier_read_requests
- Parent secret key validation request reset circuit (for nullifier keys):
nullifier_key_validation_request_contexts
- Transient note reset circuit:
note_hash_contexts
andnullifier_contexts
Verifying other data.
This section follows the same process as outlined in the inner private kernel circuit.
PrivateInputs
PreviousKernel
The format aligns with the PreviousKernel
of the inner private kernel circuit.
Hints for Read Request Reset Private Kernel Circuit
Field | Type | Description |
---|---|---|
reset_type | note_hash | nullifier | The type of read requests to be reset. |
transient_read_indices | [field ; N ] | Indices of the read requests for transient values. |
pending_value_indices | [field ; N ] | Indices of the values for transient reads. |
persistent_read_indices | [field ; M ] | Indices of the read requests for settled values. |
read_request_membership_witnesses | [MembershipWitness ; M ] | Membership witnesses for the settled values. |
read_request_statuses | [ReadRequestStatus ; C ] | Statuses of the values being read. C equals MAX_NOTE_HASH_READ_REQUESTS_PER_TX when reset_type is note_hash ; MAX_NULLIFIER_READ_REQUESTS_PER_TX when reset_type is nullifier . |
There can be multiple versions of the read request reset private kernel circuit, each with a different values of
N
andM
.
ReadRequestStatus
Field | Type | Description |
---|---|---|
state | persistent | transient | nada | State of the read request. |
index | field | Index of the hint for the read request. |
Hints for Parent Secret Key Validation Request Reset Private Kernel Circuit
Field | Type | Description |
---|---|---|
master_secret_keys | [field ; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX ] | Master secret keys for the secret keys. |
Hints for Transient Note Reset Private Kernel Circuit
Field | Type | Description |
---|---|---|
transient_nullifier_indices | [field ; MAX_NEW_NOTE_HASHES_PER_TX ] | Indices of the nullifiers for transient notes. |
nullifier_index_hints | [field ; MAX_NEW_NULLIFIERS_PER_TX ] | Indices of the transient_nullifier_indices for transient nullifiers. |
encrypted_note_preimage_hash_index_hints | [field ; MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX ] | Indices of the encrypted_note_preimage_hash_contexts for transient preimage hashes. |
log_note_hash_hints | [field ; MAX_NEW_NOTE_HASHES_PER_TX ] | Indices of the note_hash_contexts for transient preimage hashes. |
PublicInputs
The format aligns with the PublicInputs
of the initial private kernel circuit.