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_indexat indexiinhints.persistent_read_indices:- If the
persistent_read_indexequals the length of thetarget_read_requestsarray, there is no read request to be verified. Skip the rest. - Locate the
read_requestusing 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
Nread requests will be cleared, whereNis the length of thepersistent_read_indicesarray. 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_indexat indexiinhints.transient_read_indices:- If the
transient_read_indexequals the length of thetarget_read_requestsarray, there is no read request to be verified. Skip the rest. - Locate the
read_requestusing the index:read_request = target_read_requests[transient_read_index]
- Locate the
targetbeing 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.valueread_request.contract_address == target.contract_addressread_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_requestat indexiin thetarget_read_requests, find itsstatusathints.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 key_validation_request_contexts, locate the master_secret_key at master_secret_keys[i] and the relevant app_secret_key generator at app_secret_keys_generators[i], provided as hints through private_inputs.
-
If
master_secret_key == 0, ensure the request remain within thepublic_inputs.:public_inputs.transient_accumulated_data.key_validation_request_contexts[requests_kept] == request- Increase
requests_keptby 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_inputsor nullified in the same transaction.Initialize both
notes_keptandnotes_removedto0.For each
note_hashat indexiinnote_hash_contextswithin 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_hashremains within the transient_accumulated_data in thepublic_inputs:note_hash == public_inputs.transient_accumulated_data.note_hash_contexts[notes_kept] - Increment
notes_keptby 1:notes_kept += 1
- Verify that the
-
Else, locate the
nullifieratnullifier_contexts[transient_nullifier_indices[i]]:- Verify that the nullifier is associated with the note:
nullifier.contract_address == note_hash.contract_addressnullifier.note_hash_counter == note_hash.counternullifier.counter == note_hash.nullifier_counter
- Increment
notes_removedby 1:notes_removed += 1 - Ensure that an empty
note_hashis appended to the end ofnote_hash_contextsin thepublic_inputs:public_inputs.transient_accumulated_data.note_hash_contexts[N - notes_removed].is_empty() == true- Where
Nis the length ofnote_hash_contexts.
Note that the check
nullifier.counter > note_hash.counteris not necessary as thenullifier_counteris 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_keptandnullifiers_removedto0.For each
nullifierat indexiin thenullifier_contextswithin 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
nullifierremains within thetransient_accumulated_datain thepublic_inputs:nullifier == public_inputs.transient_accumulated_data.nullifier_contexts[nullifiers_kept] - Increment
nullifiers_keptby 1:nullifiers_kept += 1
- Verify that the
- Else, compute
transient_nullifier_indexastransient_nullifier_indices[nullifier_index_hints[i]]:- Verify that:
transient_nullifier_index == i - Increment
nullifiers_removedby 1:nullifiers_removed += 1 - Ensure that an empty
nullifieris appended to the end ofnullifier_contextsin thepublic_inputs:public_inputs.transient_accumulated_data.nullifier_contexts[N - nullifiers_removed].is_empty() == true- Where
Nis 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_hashesnot associated with note hashes removed in the previous step are retained within the[transient_accumulated_data](./private-kernel-initial#transientaccumulateddata)in thepublic_inputs.Initialize both
hashes_keptandhashes_removedto0.For each
preimage_hashat indexiin theencrypted_note_preimage_hash_contextswithin theprivate_inputs, find theindex_hintof its corresponding hash withinpublic_inputsatencrypted_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_hashatprivate_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]. - Verify that the
preimage_hashis associated with thenote_hash:preimage_hash.note_hash_counter == note_hash.counterpreimage_hash.contract_address == note_hash.contract_address
- Confirm that the
note_hashhas 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_removedby 1:hashes_removed += 1 - Ensure that an empty item is appended to the end of
encrypted_note_preimage_hash_contextsin thepublic_inputs:encrypted_note_preimage_hash_contexts[N - hashes_removed].is_empty() == true- Where
Nis the length ofencrypted_note_preimage_hash_contexts.
- Ensure that the associated note hash is removed:
- Else, find the
mapped_preimage_hashatencrypted_note_preimage_hash_contexts[index_hint]withinpublic_inputs:- Verify that the context is aggregated to the
public_inputscorrectly:index_hint == hashes_keptmapped_preimage_hash == preimage_hash
- Ensure that the associated note hash is retained in the
public_inputs:- Locate the
note_hashatpublic_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]. - Verify that the
preimage_hashis associated with thenote_hash:preimage_hash.note_hash_counter == note_hash.counterpreimage_hash.contract_address == note_hash.contract_address
- Locate the
- Increment
hashes_keptby 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):
key_validation_request_contexts - Transient note reset circuit:
note_hash_contextsandnullifier_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
NandM.
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_KEY_VALIDATION_REQUESTS_PER_TX] | Master secret to try to derive app secret keys and pub keys from. |
app_secret_keys_generators | [field; MAX_KEY_VALIDATION_REQUESTS_PER_TX] | App secret key generators to assist with ^. |
Hints for Transient Note Reset Private Kernel Circuit
| Field | Type | Description |
|---|---|---|
transient_nullifier_indices | [field; MAX_NOTE_HASHES_PER_TX] | Indices of the nullifiers for transient notes. |
nullifier_index_hints | [field; MAX_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_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.