diff --git a/yellow-paper/docs/circuits/_category_.json b/yellow-paper/docs/circuits/_category_.json new file mode 100644 index 00000000000..11e261e9b2a --- /dev/null +++ b/yellow-paper/docs/circuits/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Circuits", + "position": 2, + "link": { + "type": "generated-index", + "description": "circuits..." + } +} diff --git a/yellow-paper/docs/circuits/private-function.md b/yellow-paper/docs/circuits/private-function.md new file mode 100644 index 00000000000..d55191d5590 --- /dev/null +++ b/yellow-paper/docs/circuits/private-function.md @@ -0,0 +1,45 @@ +# Private Function Circuit + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +A private function circuit is a custom circuit tailored to the needs of a specific application. This circuit should be designed to handle private data processing while generating public inputs that safeguard the application and account's intentions without compromising sensitive information. + +The logic of this circuit is flexible, yet its public inputs must adhere to a specific format. + +## Private Inputs + +The private inputs of a private function circuit are customizable. + +## Public Inputs + +The public inputs of a private function circuit will be incorporated into the private inputs of a private kernel circuit. Private kernel circuits leverage these public inputs, coupled with proof data and verification key from a private function circuit, to prove the correct execution of a private function. + +It must adhere to the following format: + +| Field | Type | Description | +| ---------------------------------- | -------------------------- | ---------------------------------------------------------------------- | +| _call_context_ | _CallContext_ | Context of the call corresponding to this function execution. | +| _args_hash_ | _field_ | Hash of the function arguments. | +| _return_values_ | [_field_; C] | Return values of this function call. | +| _read_requests_ | [_ReadRequest_; C] | Requests to read a note in the note hash tree. | +| _note_hash_contexts_ | [_NoteHashContext_; C] | New note hashes created in this function call. | +| _nullifier_contexts_ | [_NullifierContext_; C] | New nullifiers created in this function call. | +| _l2_to_l1_msg_contexts_ | [_L2L1MessageContext; C] | New L2 to L1 messages created in this function call. | +| _new_contract_contexts_ | [_ContractDataContext_; C] | Data of contracts deployed in this function call. | +| _encrypted_logs_hash_ | [_field_; N] | Hash of the encrypted logs emitted in this function call. | +| _unencrypted_logs_hash_ | [_field_; N] | Hash of the unencrypted logs emitted in this function call. | +| _encrypted_log_preimages_length_ | [_field_; N] | Length of the encrypted log preimages emitted in this function call. | +| _unencrypted_log_preimages_length_ | [_field_; N] | Length of the unencrypted log preimages emitted in this function call. | +| _private_call_stack_hashes_ | [_field_; C] | Hashes of the private function calls initiated by this function. | +| _public_call_stack_hashes_ | [_field_; C] | Hashes of the public function calls initiated by this function. | +| _block_header_ | _BlockHeader_ | Information about the trees used for the transaction. | +| _chain_id_ | _field_ | Chain ID of the transaction. | +| _version_ | _field_ | Version of the transaction. | + +> The above **C**s represent constants defined by the protocol. Each **C** might have a different value from the others. + +> The above **N**s represent the number of _field_ of a hash. Its value depends on the hash function chosen by the protocol. diff --git a/yellow-paper/docs/circuits/private-kernel-initial.md b/yellow-paper/docs/circuits/private-kernel-initial.md new file mode 100644 index 00000000000..71eeec5cb33 --- /dev/null +++ b/yellow-paper/docs/circuits/private-kernel-initial.md @@ -0,0 +1,258 @@ +# Private Kernel Circuit - Initial + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +In the **initial** kernel iteration, the process involves taking a transaction request and private call data, verifying their integrity, and preparing the necessary data for subsequent circuits to operate. This step is particularly beneficial due to its separation from the [inner private kernel circuit](./private-kernel-inner.md), as the first call lacks a "previous kernel" to process. Additionally, it executes tasks that are pertinent to a transaction and need only occur once. + +### Key Responsibilities Specific to this Circuit + +#### Validating the correspondence of function call with caller's intent. + +This entails ensuring that the following data from the private call aligns with the specifications in the transaction request: + +- Contract address. +- [Function data](#function_data). +- Function arguments. + +> Although it's not enforced in the protocol, it is customary to provide a signature signed over the transaction request and verify it in the first function call. This practice guarantees that only the party possessing the key(s) can authorize a transaction with the exact transaction request. + +#### Verifying the legitimacy of the function as the entrypoint. + +- It must be a private function. +- It must not be an internal function. + +#### Ensuring the function call is the first call. + +- It must not be a delegate call. +- It must not be a static call. + +#### Ensuring transaction uniqueness. + +- It must emit the hash of the transaction request as the **first** nullifier. + +This nullifier serves multiple purposes: + +- Identifying a transaction. +- Preventing the signature of a transaction request from being reused in another transaction. +- Generating values that should be maintained within the transaction's scope. For example, it is utilized to compute the nonces for all the note hashes in a transaction. + +> Note that the final transaction data is not deterministic for a given transaction request. The production of new notes, the destruction of notes, and various other values are likely to change based on the time and conditions when a transaction is being composed. However, the intricacies of implementation should not be a concern for the entity initiating the transaction. + +### Processing Private Function Call + +#### Ensuring the contract instance being called is deployed. + +It proves that the nullifier representing the contract exists in the contract tree. + +This nullifier is the contract address siloed with the address of a precompiled deployment contract. + +#### Ensuring the function being called exists in the contract. + +The contract address contains the contract class ID, which is a hash of the root of its function tree and additional values. This circuit leverages these characteristics to establish the validity of the function's association with the contract address. + +Each leaf of the function tree is a hash representing a function. The preimage includes: + +- Function data. +- Hash of the verification key. +- Hash of the function bytecode. + +To ensure the function's existence, the circuit executes the following steps: + +1. Computes the hash of the verification key. +2. Calculates the function leaf: `hash(...function_data, vk_hash, bytecode_hash)` +3. Derives the function tree root with the leaf and the specified sibling path. +4. Computes the contract class ID using the function tree root and additional information. +5. Generates the contract address using the contract class ID and other relevant details. +6. Validates that the contract address matches the address specified in the private call data. + +#### Verifying the private function proof. + +It verifies that the private function was executed successfully with the provided proof data, verification key, and the public inputs of the private function circuit. + +#### Verifying the public inputs of the private function circuit. + +It ensures the private function circuit's intention by checking the following: + +- The contract address for each non-empty item in the following arrays must equal the current contract address: + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - Read requests. +- The portal contract address for each non-empty L2-to-L1 message context must equal the current portal contract address. +- If the new contract contexts array is not empty, the contract address must equal the precompiled deployment contract address. +- The historical data must match the one in the constant data. + +> Ensuring the alignment of the contract addresses is crucial, as it is later used to silo the value and to establish associations with values within the same contract. + +#### Verifying the call requests. + +For both private and public call requests initiated in the current function call, it ensures that for each request at index _i_: + +- Its hash equals the value at index _i_ within the call request hashes array in private function circuit's public inputs. +- Its caller context is either empty or aligns with the call context of the current function call, including: + - _msg_sender_ + - Storage contract address. + +> It is important to note that the caller context in a call request may be empty for standard calls. This precaution is crucial to prevent information leakage, particularly as revealing the _msg_sender_ to the public could pose security risks when calling a public function. + +#### Verifying the counters. + +It verifies that each relevant value is associated with a legitimate counter. + +1. For the current call: + + - The _counter_start_ must be 0. + - The _counter_end_ must be greater than the _counter_start_. + +2. For private call requests: + + - The _counter_end_ of each request must be greater than its _counter_start_. + - The _counter_start_ of the first request must be greater than the _counter_start_ of the current call. + - The _counter_start_ of the second and subsequent requests must be greater than the _counter_end_ of the previous request. + - The _counter_end_ of the last request must be less than the _counter_end_ of the current call. + +3. For items in each ordered array created in the current call: + + - The counter of the first item much be greater than the _counter_start_ of the current call. + - The counter of each subsequent item much be greater than the counter of the previous item. + - The counter of the last item much be less than the _counter_end_ of the current call. + + The ordered arrays include: + + - Note hash contexts. + - Nullifier contexts. + - New contract contexts. + - Read requests. + - Public call requests. + + > Note that _counter_start_ is used in the above steps for public call requests to ensure their correct ordering. At this point, the _counter_end_ of public call request is unknown. Both counters will be [recalibrated](./private-kernel-tail.md#recalibrating-counters) in the tail circuit following the simulation of all public function calls. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +It verifies that the following values align with those in the private call data: + +- Log hashes. +- Log lengths. + +#### Verifying the transient accumulated data. + +1. It ensures that the following arrays match those in the private call data: + + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - New contract contexts. + - Read requests. + - Public call requests. + +2. It checks that the following aligns with the array in the private call data, with items arranged in **reverse** order: + + - Private call requests. + + > It's important that the call requests are arranged in reverse order to ensure they are executed in chronological order. This becomes particularly crucial when calling a contract deployed earlier within the same transaction. + +3. For the note hash contexts, it also verifies that each is associated with a nullifier counter, which is provided as a hint via the private inputs. The nullifier counter can be: + + - Zero: if the note is not nullified in the same transaction. + - Greater than zero: if the note is nullified in the same transaction. + - This value must be greater than the counter of the note hash. + + > Nullifier counters are used in the [reset private kernel circuit](./private-kernel-reset.md#verifying-read-requests) to ensure a read happens **before** a transient note is nullified. + + > Zero can be used to indicate a non-existing transient nullifier, as this value can never serve as the counter of a nullifier. It corresponds to the _counter_start_ of the first function call. + +#### Verifying the constant data. + +It verifies that: + +- The transaction context matches the one in the transaction request. + +> The historical data must align with the data used in the private function circuit, as verified [earlier](#verifying-the-public-inputs-of-the-private-function-circuit). + +## Private Inputs + +### Transaction Request + +A transaction request represents the caller's intent. It contains: + +- Sender's address. +- Function data: + + - Function selector. + - Function type (private/public/unconstrained). + - A flag indicating whether the function is an internal function. + +- Hash of the function arguments. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Private Call Data + +The private call data holds details about the current private function call: + +- Contract address. +- Function data. +- Private call requests. +- Public call requests. +- Private function circuit public inputs. +- Proof of the private function circuit. +- Verification key of the private function circuit. +- Hash of the function bytecode. + +### Hints + +Data that aids in the verifications carried out in this circuit or later iterations: + +- Membership witness for the function leaf. +- Membership witness for the contract leaf. +- Transient note nullifier counters. + +## Public Inputs + +The structure of this public inputs aligns with that of the [inner private kernel circuit](./private-kernel-inner.md) and the [reset private kernel circuit](./private-kernel-reset.md). + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Accumulated Data + +It contains the result from the current function call: + +- Log hashes. +- Log lengths. + +### Transient Accumulated Data + +It includes transient data accumulated during the execution of the transaction up to this point: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Private call requests. +- Public call requests. diff --git a/yellow-paper/docs/circuits/private-kernel-inner.md b/yellow-paper/docs/circuits/private-kernel-inner.md new file mode 100644 index 00000000000..4b0f709d11c --- /dev/null +++ b/yellow-paper/docs/circuits/private-kernel-inner.md @@ -0,0 +1,282 @@ +# Private Kernel Circuit - Inner + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +Each **inner** kernel iteration processes a private function call and the results of a previous kernel iteration. + +### 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. + +The preceding proof can be: + +- [Initial private kernel proof](./private-kernel-initial.md) +- Inner private kernel proof. +- [Reset private kernel proof](./private-kernel-reset.md). + +The previous proof and the proof for the current function call are verified using recursion. + +### Processing Private Function Call + +#### Ensuring the contract instance being called is deployed. + +It proves that either of the following conditions is true: + +1. The nullifier representing the contract exists in the contract tree. + + - Specifically, this nullifier is the contract address siloed with the address of a precompiled deployment contract. + +2. The contract is listed in the new contract context within the public inputs. + + - The index of this contract in the new contracts array is supplied as a hint through private inputs. + - The counter of the new contract context must be less than the _counter_start_ of the current call. + +#### Ensuring the function being called exists in the contract. + +The contract address contains the contract class ID, which is a hash of the root of its function tree and additional values. This circuit leverages these characteristics to establish the validity of the function's association with the contract address. + +Each leaf of the function tree is a hash representing a function. The preimage includes: + +- Function data. +- Hash of the verification key. +- Hash of the function bytecode. + +To ensure the function's existence, the circuit executes the following steps: + +1. Computes the hash of the verification key. +2. Calculates the function leaf: `hash(...function_data, vk_hash, bytecode_hash)` +3. Derives the function tree root with the leaf and the specified sibling path. +4. Computes the contract class ID using the function tree root and additional information. +5. Generates the contract address using the contract class ID and other relevant details. +6. Validates that the contract address matches the address specified in the private call data. + +#### Ensuring the function is legitimate: + +- It must be a private function. + +#### Ensuring the current call matches the call request. + +The top item in the previous iteration's private call requests must pertain to the current function call. + +This circuit will pop the request from the stack, comparing the hash with that of the current function call. + +The preimage of the hash contains: + +- Contract address. +- Function data. +- Private function circuit's public inputs. + +#### Ensuring this function is called with the correct context. + +1. If it is a standard call: + + - The storage contract address of the current iteration must be the same as its contract address. + - The _msg_sender_ of the current iteration must be the same as the caller's contract address. + +2. If it is a delegate call: + + - The caller context in the call request must not be empty. Specifically, the following values of the caller should not be zeros: + - _msg_sender_. + - Storage contract address. + - The _msg_sender_ of the current iteration must equal the caller's _msg_sender_. + - The storage contract address of the current iteration must equal the caller's storage contract address. + - The storage contract address of the current iteration must NOT equal the contract address. + +3. If it is an internal call: + + - The _msg_sender_ of the current iteration must equal the storage contract address. + +#### Verifying the private function proof. + +It verifies that the private function was executed successfully with the provided proof data, verification key, and the public inputs of the private function circuit. + +This circuit verifies this proof and [the proof for the previous function call](#verifying-the-previous-kernel-proof) using recursion, and generates a single proof. This consolidation of multiple proofs into one is what allows the private kernel circuits to gradually merge private function proofs into a single proof of execution that represents the entire private section of a transaction. + +#### Verifying the public inputs of the private function circuit. + +It ensures the private function circuit's intention by checking the following: + +- The contract address for each non-empty item in the following arrays must equal the storage contract address of the current call: + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - Read requests. +- The portal contract address for each non-empty L2-to-L1 message must equal the portal contract address of the current call. +- If the new contract contexts array is not empty, the contract address must equal the precompiled deployment contract address. +- The historical data must match the one in the constant data. + +> Ensuring the alignment of the contract addresses is crucial, as it is later used to silo the value and to establish associations with values within the same contract. + +If it is a static call, it must ensure that the function does not induce any state changes by verifying that the following arrays are empty: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. + +#### Verifying the call requests. + +For both private and public call requests initiated in the current function call, it ensures that for each request at index _i_: + +- Its hash equals the value at index _i_ within the call request hashes array in private function circuit's public inputs. +- Its caller context is either empty or aligns with the call context of the current function call, including: + - _msg_sender_ + - Storage contract address. + +> It is important to note that the caller context in a call request may be empty for standard calls. This precaution is crucial to prevent information leakage, particularly as revealing the _msg_sender_ to the public could pose security risks when calling a public function. + +#### Verifying the counters. + +It verifies that each relevant value is associated with a legitimate counter. + +1. For the current call: + + - The _counter_end_ of the current call must be greater than its _counter_start_. + - Both counters must match the ones defined in the top item in the previous iteration's private call requests. + +2. For private call requests in the private call data: + + - The _counter_end_ of each request must be greater than its _counter_start_. + - The _counter_start_ of the first request must be greater than the _counter_start_ of the current call. + - The _counter_start_ of the second and subsequent requests must be greater than the _counter_end_ of the previous request. + - The _counter_end_ of the last request must be less than the _counter_end_ of the current call. + +3. For items in each ordered array in the private call data: + + - The counter of the first item much be greater than the _counter_start_ of the current call. + - The counter of each subsequent item much be greater than the counter of the previous item. + - The counter of the last item much be less than the _counter_end_ of the current call. + + The ordered arrays include: + + - Note hash contexts. + - Nullifier contexts. + - New contract contexts. + - Read requests. + - Public call requests. + + > Note that _counter_start_ is used in the above steps for public call requests to ensure their correct ordering. At this point, the _counter_end_ of public call request is unknown. Both counters will be [recalibrated](./private-kernel-tail.md#recalibrating-counters) in the tail circuit following the simulation of all public function calls. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +It checks that the hashes and the lengths for both encrypted and unencrypted logs are accumulated as follows: + +- New log hash = `hash(prev_hash, cur_hash)` + - If either hash is zero, the new hash will be `prev_hash | cur_hash` +- New log length = `prev_length + cur_length` + +#### Verifying the transient accumulated data. + +1. It verifies that the following values match the result of combining the values in the previous iteration's public inputs with those in the private call data: + + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - New contract contexts. + - Read requests. + - Public call requests. + +2. For the newly added note hashes from private function circuits' public inputs, this circuit also checks that each is associated with a nullifier counter, provided as a hint via the private inputs. The nullifier counter can be: + + - Zero: if the note is not nullified in the same transaction. + - Greater than zero: if the note is nullified in the same transaction. + - This value must be greater than the counter of the note hash. + + > Nullifier counters are used in the [reset private kernel circuit](./private-kernel-reset.md) to ensure a read happens **before** a transient note is nullified. + + > Zero can be used to indicate a non-existing transient nullifier, as this value can never serve as the counter of a nullifier. It corresponds to the _counter_start_ of the first function call. + +3. It verifies that the private call requests include: + + - All requests from the previous iteration's public inputs excluding the top one. + - All requests present in the private call data, appended to the above in **reverse** order. + + > Ensuring the chronological execution of call requests is vital, requiring them to be arranged in reverse order. This becomes particularly crucial when calling a contract deployed earlier within the same transaction. + +#### Verifying the constant data. + +It verifies that the constant data matches the one in the previous iteration's public inputs. + +## Private Inputs + +### Previous Kernel + +The data of the previous kernel iteration: + +- Proof of the kernel circuit. It must be one of the following: + - [Initial private kernel circuit](./private-kernel-initial.md). + - Inner private kernel circuit. + - [Reset private kernel circuit](./private-kernel-reset.md). +- Public inputs of the proof. +- Verification key of the kernel circuit. +- Membership witness for the verification key. + +### Private Call Data + +The private call data holds details about the current private function call: + +- Contract address. +- Function data. +- Private call requests. +- Public call requests. +- Private function circuit public inputs. +- Proof of the private function circuit. +- Verification key of the private function circuit. +- Hash of the function bytecode. + +### Hints + +Data that aids in the verifications carried out in this circuit or later iterations: + +- Index of the new contract. +- Membership witness for the function leaf. +- Membership witness for the contract leaf. +- Transient note nullifier counters. + +## Public Inputs + +The structure of this public inputs aligns with that of the [initial private kernel circuit](./private-kernel-initial.md) and the [reset private kernel circuit](./private-kernel-reset.md). + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Accumulated Data + +It contains data accumulated during the execution of the transaction up to this point: + +- Log hashes. +- Log lengths. + +### Transient Accumulated Data + +It includes transient data accumulated during the execution of the transaction up to this point: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Private call requests. +- Public call requests. diff --git a/yellow-paper/docs/circuits/private-kernel-reset.md b/yellow-paper/docs/circuits/private-kernel-reset.md new file mode 100644 index 00000000000..1f4547caa64 --- /dev/null +++ b/yellow-paper/docs/circuits/private-kernel-reset.md @@ -0,0 +1,160 @@ +# Private Kernel Circuit - Reset + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +The **reset** circuit is designed to abstain from processing individual private function calls. Instead, it injects the outcomes of an initial or inner private kernel circuit, scrutinizes the public inputs, and resets the verified data within its scope. This circuit can be executed either preceding the tail circuit or as a means to "reset" public inputs, allowing data to accumulate seamlessly in subsequent iterations. + +The incorporation of this circuit 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. + +### 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. + +The preceding proof can be: + +- [Initial private kernel proof](./private-kernel-initial.md). +- [Inner private kernel proof](./private-kernel-inner.md). + +### Verifying and Resetting Data + +#### Verifying read requests. + +A read request can pertain to one of two note types: + +- A persistent note: generated in a prior successful transaction and included in the note hash tree. +- A transient note: created in the current transaction, not yet part of the note hash tree. + +For each non-empty read request in the previous kernel's public inputs, it can be cleared if it meets either of the following conditions: + +1. When reading a persistent note, it requires a valid membership check, where: + + - The leaf corresponds to the note hash being read. + - The sibling path is provided as a hint. + - The root matches the note hash tree root in the historical data. + +2. When reading a transient note, it must have been created before the read operation: + + - Locates the note hash within the note hash contexts. + - Its index in the note hash contexts is provided as a hint through private inputs. + - The note hash must equal the note hash of the read request. + - The contract address of the note hash must equal the contract address of the read request. + - The counter of the note hash must be less than the counter of the read request. + - The nullifier counter of the note hash must be zero or a value greater than the counter of the read request. + +For reading a transient note created in a yet-to-be-processed nested execution: + +- The index provided as a hint will be the length of the note hash contexts array, indicating the transient note hasn't been added yet. +- The read request must be propagated to the public inputs. + +> Given that a reset circuit can execute between two inner circuits, there's a possibility that a transient note is created in a nested execution and hasn't been added to 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 transient note has been included in the public input. + +#### Squashing transient note hashes and nullifiers. + +In the event that a transient note is nullified within the same transaction, both its note hash and nullifier can be expunged from the public inputs. This not only avoids redundant data broadcasting but also frees up space for additional note hashes and nullifiers. + +For each nullifier associated with a non-zero nullified note hash: + +1. Finds its index in the note hash contexts using hints provided through private inputs. Proceeds no further if the index value equals the length of the note hash contexts array. +2. Locates the note hash in the note hash contexts using the identified index. +3. The note hash must equal the nullified note hash associated with the nullifier. +4. The contract address of the note hash must equal the contract address of the nullifier. +5. The nullifier counter of the note hash must equal the counter of the nullifier. + - The nullifier counter is assured to be greater than the counter of the note hash when propagated from the [initial](./private-kernel-initial.md#verifying-the-accumulated-data) or [inner](./private-kernel-inner.md#verifying-the-accumulated-data) private kernel circuits. +6. Sets both the note hash and the nullifier to zero. + +> It is imperative to set the note hash and nullifier to zeros before processing the next nullifier. This precaution prevents two nullifiers from nullifying the same note. By clearing the values, the second nullifier will identify its corresponding note hash as having a zero value, and will fail to compare its nullified note hash with a zero hash. + +> Note that an index hint can be set as the length of the note hash array to bypass the above process even when the corresponding nullifier is nullifying a transient note hash already present in the public inputs. The transient note hash must be retained in the public inputs for reading in a yet-to-be-processed nested execution. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +It ensures that the accumulated data matches the data in the previous iteration's public inputs. + +#### Verifying the transient accumulated data. + +The following must equal the result after verification or squashing: + +- Note hash contexts. +- Nullifier contexts. +- Read requests. + +The following must equal the corresponding values in the previous kernel's public inputs: + +- L2-to-L1 message contexts. +- New contract contexts. +- Private call requests. +- Public call requests. + +#### Verifying the constant data. + +It verifies that the constant data matches the one in the previous iteration's public inputs. + +## Private Inputs + +### Previous Kernel + +The data of the previous kernel iteration: + +- Proof of the kernel circuit. It must be one of the following: + - [Initial private kernel circuit](./private-kernel-initial.md). + - [Inner private kernel circuit](./private-kernel-inner.md). +- Public inputs of the proof. +- Verification key of the kernel circuit. +- Membership witness for the verification key. + +### Hints + +Data that aids in the verifications carried out in this circuit: + +- Membership witnesses for persistent read requests. +- Indices of note hashes for transient read requests. +- Indices of note hashes for transient nullifiers. + +## Public Inputs + +The structure of this public inputs aligns with that of the [initial private kernel circuit](./private-kernel-initial.md) and the [inner private kernel circuit](./private-kernel-inner.md). + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Accumulated Data + +It contains data accumulated during the execution of the transaction up to this point: + +- Log hashes. +- Log lengths. + +### Transient Accumulated Data + +It includes transient data accumulated during the execution of the transaction up to this point: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Private call requests. +- Public call requests. diff --git a/yellow-paper/docs/circuits/private-kernel-tail.md b/yellow-paper/docs/circuits/private-kernel-tail.md new file mode 100644 index 00000000000..b36464a25f3 --- /dev/null +++ b/yellow-paper/docs/circuits/private-kernel-tail.md @@ -0,0 +1,224 @@ +# Private Kernel Circuit - Tail + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## 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. + +The preceding proof can be: + +- [Initial private kernel proof](./private-kernel-initial.md). +- [Inner private kernel proof](./private-kernel-inner.md). +- [Reset private kernel proof](./private-kernel-reset.md). + +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 nullifiers in the public inputs from the last initial or inner iteration. + +#### Ensuring the previous iteration is the last. + +The following must be empty to ensure all the private function calls are processed: + +- Private call requests. + +The following must be empty to ensure a comprehensive final reset: + +- The nullified note hash associated with each nullifier. +- Read requests. + +> A [reset iteration](./private-kernel-reset.md) 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. + +1. This circuit must silo the following with each item's contract address: + + - Note hash contexts. + - Nullifier contexts. + + The siloed value is computed as: `hash(contract_address, value)`. + + Siloing with a contract address 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. + +2. The circuit then applies nonces to the note hashes: + + - The nonce for a note hash is computed as: `hash(first_nullifier, index)`, where: + - `first_nullifier` is the [hash of the transaction request](./private-kernel-initial.md#ensuring-transaction-uniqueness). + - `index` is the position of the note hash in the note hashes array in the public inputs. + + Siloing with a nonce guarantees that each final note hash is a unique value in the note hash tree. + +3. Additionally, this circuit generates the final hashes for L2-L1 messages, calculated as: + + `hash(contract_address, version_id, portal_contract_address, chain_id, message)` + + Where _version_id_ and _portal_contract_address_ equal the values defined in the constant data. + +#### Verifying ordered arrays. + +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 arrays (_ordered_array_) in public inputs: + +- Note hashes. +- Nullifiers. +- L2-to-l1 messages. +- Public call requests. +- New contracts (if public call request is empty). +- New contract contexts (if public call request is not empty). + +The corresponding _unordered_arrays_ for the above are sourced either from the transient accumulated data of the previous iteration or from the [siloed results](#siloing-values). + +A hints array is provided through private inputs for every _unordered_array_. + +For each hint _hints[i]_ at index _i_, this circuit locates the item at index _i_ in _ordered_array_: + +- If the item is not empty: + - It must correspond to the item at index _hints[i]_ in _unordered_array_. + - For _i_ != 0, the counter must be greater (less for public call requests) than the counter of the item at index _hints[i - 1]_ in _unordered_array_. +- If the item is empty: + - All the subsequent items must be empty in both _ordered_array_ and _unordered_array_. + +> Note that the public call requests must be arranged in descending order to ensure the calls are executed in chronological order. + +> Note that 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. + +1. For public call requests: + + The _counter_end_ for a public call request is determined by the overall count of call requests, reads and writes, note hashes and nullifiers within its scope, including those nested within its child function executions. This calculation, performed in advance of executing this circuit, provides the necessary input for the recalibration process. + + This circuit enables the adjustment of counters, ensuring that subsequent public kernels can be executed with the correct counter range. + + An array _public_call_counters_ is provided through private inputs. The reassignment process unfolds as follows: + + 1. Check that items in _public_call_counters_ are in descending order: + - The _counter_end_ of each item must be greater than its _counter_start_. + - The _counter_end_ of the second and subsequent items must be less than the _counter_start_ of the previous item. + 2. Ensure that the _counter_start_ of the last item in _public_call_counters_ is _1_. + 3. Assign the _counter_start_ and _counter_end_ of the item at index _i_ in _public_call_counters_ to the corresponding item at index _i_ in the [ordered](#verifying-ordered-arrays) public call requests. + + > It's crucial for the _counter_start_ of the last item to be _1_, as it's assumed in the [tail public kernel circuit](./public-kernel-tail.md#grouping-update-requests) that no update requests have a counter _1_. + + > While the _counter_start_ of public call request is assigned in the private function circuit to preserve the order, it's important to acknowledge that it may be modified in this step. As using _counter_start_ populated from private function circuits maybe leak information. It's recommended to adept the values that mirror the incremented amount on the public side without including any side effects on the private side. + +2. For new contract contexts: + + If there's at least one non-empty public call request, the new contract contexts will be carried forward for processing in the public kernels. However, the counters in new contract contexts must be adjusted to reflect the changes to the counters for public call requests in the previous step. + + For each new contract context in the transient accumulated data: + + 1. If its counter is greater than the **old** _counter_start_ of the public call request at index _0_, update it to be the **new** _counter_end_ of the public call request and skip the remaining steps. + 2. Find the public call request at index _i_ where the **old** _counter_start_ is greater than the counter. + 3. Check that the **old** _counter_start_ of the public call request at index _i + 1_ is less than the counter. + 4. Update its counter to be the **new** _counter_start_ of the public call request at index _i_. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +1. The following must align with the results after ordering, as verified in a [previous step](#verifying-ordered-arrays): + + - Note hashes. + - Nullifiers. + - L2-to-L1 messages. + - New contracts. + + > Note that these are arrays of siloed values or relevant data. Attributes aiding verification and siloing only exist in the corresponding types in the transient accumulated data. + +2. The following must match the respective values in the previous kernel's public inputs: + + - Log hashes. + - Log lengths. + +3. The following must be empty: + + - Old public data tree snapshot. + - New public data tree snapshot. + +#### Verifying the transient accumulated data. + +It ensures that all data in the transient accumulated data is empty, with the exception of the public call requests and new contract contexts. + +The public call requests must adhere to a specific order, as verified in a [previous step](#verifying-ordered-arrays). + +The new contract contexts should be empty when there are no public call requests. In the event of propagation to the public kernels, they must also [conform to a specific order](#verifying-ordered-arrays). + +#### Verifying the constant data. + +It verifies that the constant data matches the one in the previous iteration's public inputs. + +## Private Inputs + +### Previous Kernel + +The data of the previous kernel iteration: + +- Proof of the kernel circuit. It could be one of the following: + - [Initial private kernel circuit](./private-kernel-initial.md). + - [Inner private kernel circuit](./private-kernel-inner.md). + - [Reset private kernel circuit](./private-kernel-reset.md). +- Public inputs of the proof. +- Verification key of the kernel circuit. +- Membership witness for the verification key. + +### Hints + +Data that aids in the verifications carried out in this circuit: + +- Sorted indices of public call requests. +- Counters for public call requests. + +## Public Inputs + +The structure of this public inputs aligns with that of the [iterative public kernel circuit](./public-kernel-iterative.md) and the [tail public kernel circuit](./public-kernel-tail.md). + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Accumulated Data + +It contains data accumulated during the execution of the transaction: + +- Note hashes. +- Nullifiers. +- L2-to-L1 messages. +- New contracts. +- Log hashes. +- Log lengths. +- Old public data tree snapshot. +- New public data tree snapshot. + +### Transient Accumulated Data + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Update requests. +- Public call requests. diff --git a/yellow-paper/docs/circuits/public-kernel-iterative.md b/yellow-paper/docs/circuits/public-kernel-iterative.md new file mode 100644 index 00000000000..4745ebec840 --- /dev/null +++ b/yellow-paper/docs/circuits/public-kernel-iterative.md @@ -0,0 +1,280 @@ +# Public Kernel Circuit - Iterative + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +In the public kernel iteration, the process involves taking a previous iteration and public call data, verifying their integrity, and preparing the necessary data for subsequent circuits to operate. + +### 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. + +The preceding proof can be: + +- [tail private kernel proof](./private-kernel-tail.md). +- Iterative public kernel proof. + +### Processing Public Function Call + +#### Ensuring the contract instance being called is deployed. + +1. The nullifier representing the contract exists in the contract tree. + + - Specifically, this nullifier is the contract address siloed with the address of a precompiled deployment contract. + +2. The contract is listed in the new contracts within the public inputs. + + - The index of this contract in the new contracts array is supplied as a hint through private inputs. + - The counter of the new contract context must be less than the _counter_start_ of the current call. + +#### Ensuring the function being called exists in the contract. + +The contract address contains the contract class ID, which is a hash of the root of its function tree and additional values. This circuit leverages these characteristics to establish the validity of the function's association with the contract address. + +Each leaf of the function tree is a hash representing a function. The preimage includes: + +- Function data. +- Hash of the verification key. +- Hash of the function bytecode. + +To ensure the function's existence, the circuit executes the following steps: + +1. Computes the hash of the verification key. +2. Calculates the function leaf: `hash(...function_data, vk_hash, bytecode_hash)` +3. Derives the function tree root with the leaf and the specified sibling path. +4. Computes the contract class ID using the function tree root and additional information. +5. Generates the contract address using the contract class ID and other relevant details. +6. Validates that the contract address matches the address specified in the call data. + +#### Ensuring the function is legitimate: + +- It must be a public function. + +#### Ensuring the current call matches the call request. + +The top item in the previous iteration's public call requests must pertain to the current function call. + +This circuit will pop the request from the stack, comparing the hash with that of the current function call. + +The preimage of the hash contains: + +- Contract address. +- Function data. +- Public function circuit's public inputs. + +#### Ensuring this function is called with the correct context. + +1. If it is a standard call: + + - The storage contract address of the current iteration must be the same as its contract address. + - The _msg_sender_ of the current iteration must be the same as the caller's contract address. + +2. If it is a delegate call: + + - The caller context in the call request must not be empty. Specifically, the following values of the caller should not be zeros: + - _msg_sender_. + - Storage contract address. + - The _msg_sender_ of the current iteration must equal the caller's _msg_sender_. + - The storage contract address of the current iteration must equal the caller's storage contract address. + - The storage contract address of the current iteration must NOT equal the contract address. + +3. If it is an internal call: + + - The _msg_sender_ of the current iteration must equal the storage contract address. + +#### Verifying the public function proof. + +It verifies that the public function was executed with the provided proof data, verification key, and the public inputs of the VM circuit. The result of the execution is specified in the public inputs, which will be used in subsequent steps to enforce the conditions they must satisfy. + +#### Verifying the public inputs of the public function circuit. + +It ensures the function's intention by checking the following: + +- The contract address for each non-empty item in the following arrays must equal the storage contract address of the current call: + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - Read requests. + - Update requests. +- The portal contract address for each non-empty L2-to-L1 message must equal the portal contract address of the current call. + +> Ensuring the alignment of the contract addresses is crucial, as it is later used to silo the value and to establish associations with values within the same contract. + +If it is a static call, it must ensure that the function does not induce any state changes by verifying that the following arrays are empty: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- Update requests. + +#### Verifying the call requests. + +For the public call requests initiated in the current function call, it ensures that for each request at index _i_: + +- Its hash equals the value at index _i_ within the call request hashes array in public function circuit's public inputs. +- If the hash is not zero, its caller context must align with the call context of the current function call, including: + - _msg_sender_ + - Storage contract address. + +#### Verifying the counters. + +It verifies that each relevant value is associated with a legitimate counter. + +1. For the current call: + + - The _counter_end_ of the current call must be greater than its _counter_start_. + - Both counters must match the ones defined in the top item in the previous iteration's public call requests. + +2. For the public call requests: + + - The _counter_end_ of each request must be greater than its _counter_start_. + - The _counter_start_ of the first request must be greater than the _counter_start_ of the current call. + - The _counter_start_ of the second and subsequent requests must be greater than the _counter_end_ of the previous request. + - The _counter_end_ of the last request must be less than the _counter_end_ of the current call. + +3. For items in each ordered array created in the current call: + + - The counter of the first item much be greater than the _counter_start_ of the current call. + - The counter of each subsequent item much be greater than the counter of the previous item. + - The counter of the last item much be less than the _counter_end_ of the current call. + + The ordered arrays include: + + - Read requests. + - Update requests. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +1. It ensures that the following values match those in the previous iteration's public inputs: + + - Note hashes. + - Nullifiers. + - L2-to-L1 messages. + - New contracts. + - **Encrypted** log hash. + - **Encrypted** log length. + - Old public data tree snapshot. + - New public data tree snapshot. + +2. It checks that the hash and the length for **unencrypted** logs are accumulated as follows: + + - New log hash = `hash(prev_hash, cur_hash)` + - If either hash is zero, the new hash will be `prev_hash | cur_hash` + - New log length = `prev_length + cur_length` + +#### Verifying the transient accumulated data. + +1. It verifies that the following values match the result of combining the values in the previous iteration's public inputs with those in the public function circuit's public inputs: + + - Note hash contexts. + - Nullifier contexts. + - L2-to-L1 message contexts. + - Read requests. + - Update requests. + +2. For the newly added update requests from public function circuit's public inputs, this circuit also checks that each is associated with an override counter, provided as a hint via the private inputs. This override counter can be: + + - Zero: if the slot does not change later in the same transaction. + - Greater than zero: if the slot is updated later in the same transaction. + - It pertains to a subsequent update request altering the same slot. Therefor, the counter value must be greater than the counter of the update request. + + > Override counters are used in the [tail public kernel circuit](./public-kernel-tail.md) to ensure a read happens **before** the value is changed in a later update. + + > Zero serves as an indicator for an unchanged update, as this value can never act as the counter of an update request. It corresponds to the _counter_start_ of the first function call. + +3. It verifies that the public call requests include: + + - All requests from the previous iteration's public inputs except for the top one. + - All requests present in the public call data, appended to the above in **reverse** order. + +#### Verifying the constant data. + +It verifies that the constant data matches the one in the previous iteration's public inputs. + +## Private Inputs + +### Previous Kernel + +The data of the previous kernel iteration: + +- Proof of the kernel circuit. It could be one of the following: + - [Tail private kernel circuit](./private-kernel-tail.md). + - Iterative public kernel circuit. +- Public inputs of the proof. +- Verification key of the kernel circuit. +- Membership witness for the verification key. + +### Public Call Data + +The call data holds details about the current public function call: + +- Contract address. +- Function data. +- Public call requests. +- Public function circuit public inputs. +- Proof of the public function circuit. +- Verification key. +- Hash of the function bytecode. + +### Hints + +Data that aids in the verifications carried out in this circuit or later iterations: + +- Index of the new contract. +- Membership witness for the function leaf. +- Membership witness for the contract leaf. +- Update requests override counters. + +## Public Inputs + +The structure of this public inputs aligns with that of the [tail private kernel circuit](./private-kernel-tail.md) and the [tail public kernel circuit](./public-kernel-tail.md). + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Accumulated Data + +It contains data accumulated during the execution of the transaction up to this point: + +- Note hashes. +- Nullifiers. +- L2-to-L1 messages. +- New contracts. +- Log hashes. +- Log lengths. +- Old public data tree snapshot. +- New public data tree snapshot. + +### Transient Accumulated Data + +It includes data from the current function call, aggregated with the results from the previous iterations: + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Update requests. +- Public call requests. diff --git a/yellow-paper/docs/circuits/public-kernel-tail.md b/yellow-paper/docs/circuits/public-kernel-tail.md new file mode 100644 index 00000000000..38fd39a28c0 --- /dev/null +++ b/yellow-paper/docs/circuits/public-kernel-tail.md @@ -0,0 +1,312 @@ +# Public Kernel Circuit - Tail + +:::info Disclaimer +This is a draft. These requirements need to be considered by the wider team, and might change significantly before a mainnet release. +::: + +## Requirements + +The **tail** circuit refrains from processing individual public function calls. Instead, it integrates the results of iterative public kernel circuit and performs additional verification and processing necessary for generating the final public inputs. + +### 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. + +The preceding proof can only be: + +- [Iterative public kernel proof](./public-kernel-iterative.md). + +#### Ensuring the previous iteration is the last. + +The following must be empty to ensure all the public function calls are processed: + +- Public call requests. + +### Processing Final Outputs + +#### Siloing values. + +1. It silos the following in the transient accumulated data with each item's contract address: + + - Note hash contexts. + - Nullifier contexts. + + The siloed value is computed as: `hash(contract_address, value)`. + + Siloing with a contract address 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. + +2. It then applies nonces to the note hashes: + + - The nonce for a note hash is computed as: `hash(first_nullifier, index)`, where: + - `first_nullifier` is the hash of the transaction request. + - `index` is the position of the note hash in the note hashes array in the public inputs. + + Siloing with a nonce guarantees that each final note hash is a unique value in the note hash tree. + +3. It generates the final hashes for L2-L1 messages, calculated as: + + `hash(contract_address, version_id, portal_contract_address, chain_id, message)` + + Where _version_id_ and _portal_contract_address_ equal the values defined in the constant data. + +4. It silos the storage slot of each item in the following array with the item's contract address: + + - Read requests. + - Update requests. + + The siloed storage slot is computed as: `hash(contract_address, storage_slot)`. + +> While siloing could occur in each kernel iteration, it is _typically_ more efficient to be done once in the tail circuit. + +#### Verifying ordered arrays. + +The iterations of the public kernel may yield values in an unordered state due to the serial nature of the kernel, which contrasts with the stack-based nature of code execution. + +This circuit ensures the correct ordering of the following: + +- Note hashes. +- Nullifiers. +- L2-to-L1 messages. +- New contracts. +- Read requests. +- Update requests. + +The corresponding _unordered_arrays_ for the above are sourced from the [siloed results](#siloing-values). + +An _ordered_requests_ array and a _hints_ array are provided for every _unordered_array_ via private inputs. + +For each hint _hints[i]_ at index _i_, locate the item at index _i_ in _ordered_array_: + +- If the item is not empty: + - It must correspond to the item at index _hints[i]_ in _unordered_array_. + - For _i_ != 0, the counter must be greater than the counter of the item at index _hints[i - 1]_ in _unordered_array_. +- If the item is empty: + - All the subsequent items (index >= _i_) must be empty in both _ordered_array_ and _unordered_array_. + +#### Verifying public data snaps. + +The public data snaps array is provided through private inputs, serving as hints for read requests to prove that the value in the tree aligns with the read operation. For update requests, it substantiates the presence or absence of the storage slot in the public data tree. + +A public data snap contains: + +- A leaf in the public data tree, containing the storage slot and its value. +- An override counter, indicating the counter of an update request that overrides the value of the storage slot. Zero if the value is not overridden in this transaction. +- A flag _exists_ indicating its presence or absence in the public data tree. + +This circuit ensures the uniqueness of each snap within the provided public data snaps array. It verifies that the storage slot of each item (except for the one at index 0) must be greater than the storage slot of the previous item in the array. + +> It is crucial for each snap to be unique, as duplicated snaps would disrupt a group of update requests for the same storage slot. This could facilitate the unauthorized act of reading the old value after it has been updated. + +#### Grouping update requests. + +To facilitate the verification of read requests and streamline update requests, it is imperative to establish connections between update requests targeting the same storage slot. Furthermore, the first update request in a group must be linked to a public data snap, ensuring the dataset has progressed from the right initial state. + +A new field, _prev_counter_, is introduced to the ordered update requests to indicate whether each request possesses a previous snap or update request. Another field, _exists_, is also added to signify the presence or absence of the storage slot in the tree. + +1. For each non-empty public data snap: + + - Skip the remaining steps if its override counter is _0_. + - Locate the request within the update requests using an index provided as a hint through private inputs. + - Verify that the storage slot of the request matches the storage slot of the snap. + - Verify that the counter of the request matches the override counter of the snap. + - Ensure that the _prev_counter_ of the request is _0_. + - Set the _prev_counter_ of the request to _1_. + - Set the _exists_ flag of the request to be the same as the snap. + + > The value _1_ can be utilized to signify a public data snap, as this value can never serve as the counter of an update request. The _counter_start_ for the first public function call must be greater than or equal to 1. Subsequently, the counters for all subsequent function calls and requests should exceed this initial value. + +2. For each non-empty update request, + + - Skip the remaining steps if its override counter is _0_. + - Locate the request within the update requests using an index provided as a hint through private inputs. + - Verify that the storage slot of the request matches the storage slot of the current request. + - Verify that the counter of the request matches the override counter of the current request. + - Ensure that the _prev_counter_ of the request is _0_. + - Set the _prev_counter_ of the request to the counter of the current request. + - Set the _exists_ flag of the request to be the same as the current request. + +3. Following the previous two steps, verify that all non-empty update requests have a non-zero _prev_counter_. + +#### Verifying read requests. + +A read request can be reading: + +- An updated value: initialized or updated in the current transaction. The value being read is in an update request. +- An existing value: initialized or updated in a prior successful transaction. The value being read is the value in the public data tree. +- An uninitialized value: not initialized yet. The read request is reading the value zero. There isn't a leaf in the public data tree representing its storage slot, nor in the update requests. + +For each non-empty read request, it must satisfy one of the following conditions: + +1. If reading an updated value, the value is in an update request: + + - Locates the update request within the update requests. + - Its index in the update requests array is provided as a hint through private inputs. + - The storage slot and value of the read request must match those of the update request. + - The counter of the update request must be less than the counter of the read request. + - The override counter of the update request must be zero or greater than the counter of the read request. + + > A zero override counter indicates that the value is not overridden in the transaction. + +2. If reading an existing or an uninitialized value, the value is in a public data snap: + + - Locate the snap within the public data snaps. + - Its index in the public data snaps array is provided as a hint through private inputs. + - The storage slot and value of the read request must match those of the snap. + - The override counter of the snap must be zero or greater than the counter of the read request. + + Depending on the value of the _exists_ flag in the snap, verify its presence or absence in the public data tree: + + - If _exists_ is true: + - It must pass a membership check on the leaf. + - If _exists_ is false: + - The value must be zero. + - It must pass a non-membership check on the low leaf. + + > The membership checks are executed against the root in **old** public data tree snapshot, as defined in the public inputs. The membership witnesses for the leaves and the low leaves are provided as hints through private inputs. + +#### Updating the public data tree. + +It updates the current public data tree with the update requests. For each non-empty request in the **ordered** and **siloed** update requests array, the circuit processes it base on its type: + +1. Transient update. + + If the override counter of a request is not zero, the value is overridden by another update request that occurs later in the same transaction. This transient value can be ignored as the final state of the tree won't be affected by it. + +2. Updating an existing storage slot. + + For a non-transient update request, if the _exists_ flag is true, it is updating an existing storage slot. The circuit does the following for such an update: + + - Performs a membership check, where: + - The leaf contains the existing storage slot. + - The leaf's old value and the sibling path are provided as hints through private inputs. + - The root is the latest root after processing the previous request. + - Derives the new latest root with the new value in the leaf. + +3. Creating a new storage slot. + + For a non-transient update request, if the _exists_ flag is false, it is inserting to a new storage slot. The circuit adds it to a subtree: + + - Perform a membership check on the low leaf in the latest public data tree or the subtree. + - The leaf preimage and its membership witness are provided as hints through private inputs. + - Update the low leaf to point to the new leaf containing the new storage slot. + - The low leaf could be in the public data tree or the subtree. + - Append the new leaf to the subtree. + +After all the update requests are processed: + +- Batch insert the subtree to the public data tree. + - The insertion index is the index in the **old** public data tree snapshot. +- Verify that the latest root matches the root in the **new** public data tree snapshot in the public inputs. +- Verify that the index in the **new** public data tree snapshot equals the index in the **old** public data tree snapshot plus the number of the new leaves appended to the subtree. + +### Validating Public Inputs + +#### Verifying the accumulated data. + +1. The following must align with the results after ordering, as verified in a [previous step](#verifying-ordered-arrays): + + - Note hashes. + - Nullifiers. + - L2-to-L1 messages. + - New contracts. + + > Note that these are arrays of siloed values or relevant data. Attributes aiding verification and siloing only exist in the corresponding types in the transient accumulated data. + +2. The following must match the respective values in the previous kernel's public inputs: + + - Log hashes. + - Log lengths. + +3. The following is referenced and verified in a [previous step](#updating-the-public-data-tree): + + - Old public data tree snapshot. + - New public data tree snapshot. + +#### Verifying the transient accumulated data. + +It ensures that the transient accumulated data is empty. + +#### Verifying the constant data. + +It verifies that the constant data matches the one in the previous iteration's public inputs. + +## Private Inputs + +### Previous Kernel + +The data of the previous kernel iteration: + +- Proof of the kernel circuit. It must be: + - [Iterative public kernel circuit](./public-kernel-iterative.md). +- Public inputs of the proof. +- Verification key of the circuit. +- Membership witness for the verification key. + +### Hints + +Data that aids in the verifications carried out in this circuit: + +- Sorted indices of read requests. +- Ordered read requests. +- Sorted indices of update requests. +- Ordered update requests. +- Public data snaps. +- Indices of update requests for public data snaps. +- Indices of update requests for transient update requests. +- Hints for read requests, including: + - A flag indicating whether it's reading an update request or a leaf in the public data tree. + - Index of the update request or a public data snap. + - Membership witness. +- Indices of update requests for transient updates. +- Membership witnesses for update requests. +- Membership witnesses of low leaves in public data tree for update requests. +- Membership witnesses of low leaves in subtree for update requests. + +## Public Inputs + +The structure of this public inputs aligns with that of the [tail private kernel circuit](./private-kernel-tail.md) and the [iterative public kernel circuit](./public-kernel-iterative.md). + +### Accumulated Data + +It contains data accumulated during the execution of the entire transaction: + +- Note hashes. +- Nullifiers. +- L2-to-L1 messages. +- New contracts. +- Log hashes. +- Log lengths. +- Old public data tree snapshot. +- New public data tree snapshot. + +### Constant Data + +These are constants that remain the same throughout the entire transaction: + +- Historical data - representing the states of the block at which the transaction is constructed, including: + - Hash of the global variables. + - Roots of the trees: + - Note hash tree. + - Nullifier tree. + - Contract tree. + - L1-to-l2 message tree. + - Public data tree. +- Transaction context + - A flag indicating whether it is a fee paying transaction. + - A flag indicating whether it is a fee rebate transaction. + - Chain ID. + - Version of the transaction. + +### Transient Accumulated Data + +It includes data that aids in processing each kernel iteration. They must be empty for this circuit. + +- Note hash contexts. +- Nullifier contexts. +- L2-to-L1 message contexts. +- New contract contexts. +- Read requests. +- Update requests. +- Public call requests.