diff --git a/yellow-paper/docs/circuits/private-function.md b/yellow-paper/docs/circuits/private-function.md index f7fb5a2eed5..a5a93c29a0c 100644 --- a/yellow-paper/docs/circuits/private-function.md +++ b/yellow-paper/docs/circuits/private-function.md @@ -16,24 +16,23 @@ The public inputs of a private function circuit will be incorporated into the pr The following format defines the ABI that is used by the private kernel circuit when processing private function public inputs: -| Field | Type | Description | -| ---------------------------------- | ------------------------------------ | ---------------------------------------------------------------------- | -| _call_context_ | _[CallContext](#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](#readrequest)_; _C_] | Requests to read notes in the note hash tree. | -| _note_hashes_ | [_[NoteHash](#notehash)_; _C_] | New note hashes created in this function call. | -| _nullifiers_ | [_[Nullifier](#nullifier)_; _C_] | New nullifiers created in this function call. | -| _l2_to_l1_messages_ | [_field_; _C_] | New L2 to L1 messages created in this function call. | -| _encrypted_logs_hash_ | _field_ | Hash of the encrypted logs emitted in this function call. | -| _unencrypted_logs_hash_ | _field_ | Hash of the unencrypted logs emitted in this function call. | -| _encrypted_log_preimages_length_ | _field_ | Length of the encrypted log preimages emitted in this function call. | -| _unencrypted_log_preimages_length_ | _field_ | Length of the unencrypted log preimages emitted in this function call. | -| _private_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the private function calls initiated by this function. | -| _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | -| _block_header_ | _[BlockHeader](#blockheader)_ | Information about the trees used for the transaction. | -| _chain_id_ | _field_ | Chain ID of the transaction. | -| _version_ | _field_ | Version of the transaction. | +| Field | Type | Description | +| -------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------- | +| _call_context_ | _[CallContext](#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](#readrequest)_; _C_] | Requests to read notes in the note hash tree. | +| _note_hashes_ | [_[NoteHash](#notehash)_; _C_] | New note hashes created in this function call. | +| _nullifiers_ | [_[Nullifier](#nullifier)_; _C_] | New nullifiers created in this function call. | +| _l2_to_l1_messages_ | [_field_; _C_] | New L2 to L1 messages created in this function call. | +| _unencrypted_log_hashes_ | [_[UnencryptedLogHash](#unencryptedloghash)_; _C_] | Hashes of the unencrypted logs emitted in this function call. | +| _encrypted_log_hashes_ | [_[EncryptedLogHash](#encryptedloghash)_; _C_] | Hashes of the encrypted logs emitted in this function call. | +| _encrypted_note_preimage_hashes_ | [_[EncryptedNotePreimageHash](#encryptednotepreimagehash)_; _C_] | Hashes of the encrypted note preimages emitted in this function call. | +| _private_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the private function calls initiated by this function. | +| _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | +| _block_header_ | _[BlockHeader](#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. @@ -65,10 +64,37 @@ The following format defines the ABI that is used by the private kernel circuit #### _Nullifier_ -| Field | Type | Description | -| --------- | ------- | ------------------------------------------- | -| _value_ | _field_ | Value of the nullifier. | -| _counter_ | _field_ | Counter at which the nullifier was created. | +| Field | Type | Description | +| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | +| _value_ | _field_ | Value of the nullifier. | +| _counter_ | _field_ | Counter at which the nullifier was created. | +| _note_hash_counter_ | _field_ | Counter of the transient note the nullifier is created for. 0 if the nullifier does not associate with a transient note. | + +#### _UnencryptedLogHash_ + +| Field | Type | Description | +| --------- | ------- | -------------------------------------- | +| _hash_ | _field_ | Hash of the unencrypted log. | +| _length_ | _field_ | Number of fields of the log preimage. | +| _counter_ | _field_ | Counter at which the hash was emitted. | + +#### _EncryptedLogHash_ + +| Field | Type | Description | +| ------------ | ------- | -------------------------------------------- | +| _hash_ | _field_ | Hash of the encrypted log. | +| _length_ | _field_ | Number of fields of the log preimage. | +| _randomness_ | _field_ | A random value to hide the contract address. | +| _counter_ | _field_ | Counter at which the hash was emitted. | + +#### _EncryptedNotePreimageHash_ + +| Field | Type | Description | +| ------------------- | ------- | --------------------------------------- | +| _hash_ | _field_ | Hash of the encrypted note preimage. | +| _length_ | _field_ | Number of fields of the note preimage. | +| _counter_ | _field_ | Counter at which the hash was emitted. | +| _note_hash_counter_ | _field_ | Counter of the corresponding note hash. | #### _BlockHeader_ diff --git a/yellow-paper/docs/circuits/private-kernel-initial.md b/yellow-paper/docs/circuits/private-kernel-initial.md index 0f32e03456b..462c9fbf1cb 100644 --- a/yellow-paper/docs/circuits/private-kernel-initial.md +++ b/yellow-paper/docs/circuits/private-kernel-initial.md @@ -114,6 +114,9 @@ It verifies that each value listed below is associated with a legitimate counter - _note_hashes_ - _nullifiers_ - _read_requests_ + - _unencrypted_log_hashes_ + - _encrypted_log_hashes_ + - _encrypted_note_preimage_hashes_ 3. For the last _N_ non-empty items in the _private_call_requests_ in the _[transient_accumulated_data](#transientaccumulateddata)_: @@ -138,10 +141,6 @@ It verifies that each value listed below is associated with a legitimate counter ### Validating Public Inputs -#### Verifying the accumulated data. - -It verifies that all the values in the _[accumulated_data](#accumulateddata)_ align with the corresponding values in _[private_call](#privatecall).[call_stack_item](#privatecallstackitem).[public_inputs](./private-function.md#public-inputs)_. - #### Verifying the transient accumulated data. Within the _[public_inputs](#public-inputs)_, the _[transient_accumulated_data](#transientaccumulateddata)_ encapsulates values reflecting the operations conducted by the _private_call_. @@ -159,7 +158,13 @@ This circuit verifies that the values in _[private_inputs](#private-inputs).[pri - _read_request_contexts_ - _note_hash_, _counter_ - _public_call_requests_ - - _hash_ + - _hash_, _counter_ + - _unencrypted_log_hash_contexts_ + - _hash_, _length_, _counter_ + - _encrypted_log_hash_contexts_ + - _hash_, _length_, _randomness_, _counter_ + - _encrypted_note_preimage_hash_contexts_ + - _hash_, _length_, _counter_, _note_hash_counter_ 2. Check that the hashes in the _private_call_requests_ align with the values in the _private_call_stack_item_hashes_ in the _private_function_public_inputs_, but in **reverse** order. @@ -178,6 +183,9 @@ This circuit verifies that the values in _[private_inputs](#private-inputs).[pri - _nullifier_contexts_ - _l2_to_l1_message_contexts_ - _read_request_contexts_ + - _unencrypted_log_hash_contexts_ + - _encrypted_log_hash_contexts_ + - _encrypted_note_preimage_hash_contexts_ > Ensuring the alignment of the contract addresses is crucial, as it is later used to [silo the values](./private-kernel-tail.md#siloing-values) and to establish associations with values within the same contract. @@ -239,25 +247,19 @@ Data that remains the same throughout the entire transaction. | _block_header_ | _[BlockHeader](./private-function.md#blockheader)_ | Roots of the trees at the time the transaction was assembled. | | _tx_context_ | _[TransactionContext](#transactioncontext)_ | Context of the transaction. | -### _AccumulatedData_ - -| Field | Type | Description | -| ---------------------------------- | ------- | ---------------------------------------------------- | -| _encrypted_logs_hash_ | _field_ | Hash of the accumulated encrypted logs. | -| _unencrypted_logs_hash_ | _field_ | Hash of the accumulated unencrypted logs. | -| _encrypted_log_preimages_length_ | _field_ | Length of the accumulated encrypted log preimages. | -| _unencrypted_log_preimages_length_ | _field_ | Length of the accumulated unencrypted log preimages. | - ### _TransientAccumulatedData_ -| Field | Type | Description | -| --------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | -| _note_hash_contexts_ | [_[NoteHashContext](#notehashcontext)_; _C_] | Note hashes with extra data aiding verification. | -| _nullifier_contexts_ | [_[NullifierContext](#nullifiercontext)_; _C_] | Nullifiers with extra data aiding verification. | -| _l2_to_l1_message_contexts_ | [_[L2toL1MessageContext](#l2tol1messagecontext)_; _C_] | L2-to-l1 messages with extra data aiding verification. | -| _read_request_contexts_ | [_[ReadRequestContext](#readrequestcontext)_; _C_] | Requests to read notes in the note hash tree. | -| _private_call_requests_ | [_[CallRequest](#callrequest)_; _C_] | Requests to call private functions. | -| _public_call_requests_ | [_[CallRequest](#callrequest)_; _C_] | Requests to call publics functions. | +| Field | Type | Description | +| --------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| _note_hash_contexts_ | [_[NoteHashContext](#notehashcontext)_; _C_] | Note hashes with extra data aiding verification. | +| _nullifier_contexts_ | [_[NullifierContext](#nullifiercontext)_; _C_] | Nullifiers with extra data aiding verification. | +| _l2_to_l1_message_contexts_ | [_[L2toL1MessageContext](#l2tol1messagecontext)_; _C_] | L2-to-l1 messages with extra data aiding verification. | +| _read_request_contexts_ | [_[ReadRequestContext](#readrequestcontext)_; _C_] | Requests to read notes in the note hash tree. | +| _unencrypted_log_hash_contexts_ | [_[EncryptedLogHashContext](#encryptedloghashcontext)_; _C_] | Hashes of the unencrypted logs with extra data aiding verification. | +| _encrypted_log_hash_contexts_ | [_[UnencryptedLogHashContext](#unencryptedloghashcontext)_; _C_] | Hashes of the encrypted logs with extra data aiding verification. | +| _encrypted_note_preimage_hash_contexts_ | [_[EncryptedNotePreimageHashContext](#encryptednotepreimagehash)_; _C_] | Hashes of the encrypted note preimages with extra data aiding verification. | +| _private_call_requests_ | [_[CallRequest](#callrequest)_; _C_] | Requests to call private functions. | +| _public_call_requests_ | [_[CallRequest](#callrequest)_; _C_] | Requests to call publics functions. | > The above **C**s represent constants defined by the protocol. Each **C** might have a different value from the others. @@ -322,34 +324,63 @@ Data that remains the same throughout the entire transaction. | Field | Type | Description | | ------------------- | -------------- | -------------------------------------------------------- | | _value_ | _field_ | Hash of the note. | -| _contract_address_ | _AztecAddress_ | Address of the contract the note was created. | | _counter_ | _field_ | Counter at which the note hash was created. | | _nullifier_counter_ | _field_ | Counter at which the nullifier for the note was created. | +| _contract_address_ | _AztecAddress_ | Address of the contract the note was created. | #### _NullifierContext_ -| Field | Type | Description | -| --------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- | -| _value_ | _field_ | Value of the nullifier. | -| _contract_address_ | _AztecAddress_ | Address of the contract the nullifier was created. | -| _counter_ | _field_ | Counter at which the nullifier was created. | -| _nullified_note_hash_ | _field_ | The hash of the transient note the nullifier is created for. 0 if the nullifier does not associated with a transient note. | +| Field | Type | Description | +| ------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------ | +| _value_ | _field_ | Value of the nullifier. | +| _counter_ | _field_ | Counter at which the nullifier was created. | +| _note_hash_counter_ | _field_ | Counter of the transient note the nullifier is created for. 0 if the nullifier does not associate with a transient note. | +| _contract_address_ | _AztecAddress_ | Address of the contract the nullifier was created. | #### _L2toL1MessageContext_ | Field | Type | Description | | ------------------------- | -------------- | ------------------------------------------------ | | _value_ | _field_ | L2-to-l2 message. | -| _contract_address_ | _AztecAddress_ | Address of the contract the message was created. | | _portal_contract_address_ | _AztecAddress_ | Address of the portal contract to the contract. | +| _contract_address_ | _AztecAddress_ | Address of the contract the message was created. | #### _ReadRequestContext_ | Field | Type | Description | | ------------------ | -------------- | --------------------------------------------- | | _note_hash_ | _field_ | Hash of the note to be read. | -| _contract_address_ | _AztecAddress_ | Address of the contract the request was made. | | _counter_ | _field_ | Counter at which the request was made. | +| _contract_address_ | _AztecAddress_ | Address of the contract the request was made. | + +#### _UnencryptedLogHashContext_ + +| Field | Type | Description | +| ------------------ | -------------- | -------------------------------------------- | +| _hash_ | _field_ | Hash of the unencrypted log. | +| _length_ | _field_ | Number of fields of the log preimage. | +| _counter_ | _field_ | Counter at which the hash was emitted. | +| _contract_address_ | _AztecAddress_ | Address of the contract the log was emitted. | + +#### _EncryptedLogHashContext_ + +| Field | Type | Description | +| ------------------ | -------------- | -------------------------------------------- | +| _hash_ | _field_ | Hash of the encrypted log. | +| _length_ | _field_ | Number of fields of the log preimage. | +| _randomness_ | _field_ | A random value to hide the contract address. | +| _counter_ | _field_ | Counter at which the hash was emitted. | +| _contract_address_ | _AztecAddress_ | Address of the contract the log was emitted. | + +#### _EncryptedNotePreimageHashContext_ + +| Field | Type | Description | +| ------------------- | -------------- | -------------------------------------------- | +| _hash_ | _field_ | Hash of the encrypted note preimage. | +| _length_ | _field_ | Number of fields of the note preimage. | +| _note_hash_counter_ | _field_ | Counter of the corresponding note hash. | +| _counter_ | _field_ | Counter at which the hash was emitted. | +| _contract_address_ | _AztecAddress_ | Address of the contract the log was emitted. | #### _MembershipWitness_ diff --git a/yellow-paper/docs/circuits/private-kernel-inner.md b/yellow-paper/docs/circuits/private-kernel-inner.md index d3a7fe05051..da0fed574c6 100644 --- a/yellow-paper/docs/circuits/private-kernel-inner.md +++ b/yellow-paper/docs/circuits/private-kernel-inner.md @@ -91,6 +91,9 @@ It ensures the private function circuit's intention by checking the following in - _note_hashes_ - _nullifiers_ - _l2_to_l1_messages_ + - _unencrypted_log_hashes_ + - _encrypted_log_hashes_ + - _encrypted_note_preimage_hashes_ #### Verifying the counters. @@ -100,20 +103,6 @@ Additionally, it verifies that for the _[call_stack_item](#privatecallstackitem) ### 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_hash = hash(prev_hash, cur_hash)` - - If either hash is zero, the new hash will be `prev_hash | cur_hash`. -- `new_length = prev_length + cur_length` - -Where: - -- _new_hash_ and _new_length_ are the values in _[public_inputs](#public-inputs).[accumulated_data](./private-kernel-initial.md#accumulateddata)_. -- _prev_hash_ and _prev_length_ are the values in _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./private-kernel-initial.md#public-inputs).[accumulated_data](./private-kernel-initial.md#accumulateddata)_. -- _cur_hash_ and _cur_length_ are the values in _[private_inputs](#private-inputs).[private_call](#privatecall).[call_stack_item](./private-kernel-initial.md#privatecallstackitem).[public_inputs](./private-function.md#public-inputs)_. - #### Verifying the transient accumulated data. The _[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_ in this circuit's _[public_inputs](#public-inputs)_ includes values from both the previous iterations and the _[private_call](#privatecall)_. diff --git a/yellow-paper/docs/circuits/private-kernel-reset.md b/yellow-paper/docs/circuits/private-kernel-reset.md index c7a2c320357..7d7abb75a0e 100644 --- a/yellow-paper/docs/circuits/private-kernel-reset.md +++ b/yellow-paper/docs/circuits/private-kernel-reset.md @@ -58,43 +58,41 @@ A read request can pertain to one of two note types: ### Transient Note Reset Private Kernel Circuit. -In the event that a pending note is nullified within the same transaction, both its note hash and nullifier 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. +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. 1. Ensure that each note hash is either propagated to the _public_inputs_ or nullified in the same transaction. - Initialize both _notes_kept_ and _notes_removed_ to 0. + Initialize both _notes_kept_ and _notes_removed_ to _0_. - For each _note_hash_ at index _i_ in the _note_hash_contexts_ within the _private_inputs_, find the _transient_nullifier_index_ of its associated nullifer at _`transient_nullifier_indices[i]`_, provided as [hints](#hints-for-transient-note-reset-private-kernel-circuit): - - - If _`transient_nullifier_index == nullifier_contexts.len()`_: + For each _note_hash_ at index _i_ in _note_hash_contexts_ within the _private_inputs_, find the index of its nullifer at _`transient_nullifier_indices[i]`_, provided as [hints](#hints-for-transient-note-reset-private-kernel-circuit): + - If _`transient_nullifier_indices[i] == nullifier_contexts.len()`_: - Verify that the _note_hash_ remains within the _[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_ in the _public_inputs_: - - _`note_hash == public_inputs.transient_accumulated_data.note_hash_contexts[notes_kept]`_ + _`note_hash == public_inputs.transient_accumulated_data.note_hash_contexts[notes_kept]`_ - Increment _notes_kept_ by 1: _`notes_kept += 1`_ - - - Else, locate the _nullifier_ at _nullifier_contexts[transient_nullifier_index]_: + - Else, locate the _nullifier_ at _`nullifier_contexts[transient_nullifier_indices[i]]`_: - Verify that the nullifier is associated with the note: - _`nullifier.contract_address == note_hash.contract_address`_ - - _`nullifier.nullified_note_hash == note_hash.value`_ + - _`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 of _note_hash_contexts_ in the _public_inputs_: - _`public_inputs.transient_accumulated_data.note_hash_contexts[N - notes_removed].is_empty() == true`_ - Where _N_ is the length of _note_hash_contexts_. - > Note that the check `nullifier.counter > note_hash.counter` is not necessary as the nullifier counter is assured to be greater than the counter of the note hash when [propagated](./private-kernel-initial.md#verifying-the-transient-accumulated-data) from either the initial or inner private kernel circuits. + > Note that the check `nullifier.counter > note_hash.counter` is not necessary as the _nullifier_counter_ is assured to be greater than the counter of the note hash when [propagated](./private-kernel-initial.md#verifying-the-transient-accumulated-data) from either the initial or inner private kernel circuits. 2. Ensure that nullifiers not associated with note hashes removed in the previous step are retained within the _[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_ in the _public_inputs_. - Initialize both _nullifiers_kept_ and _nullifiers_removed_ to 0. + Initialize both _nullifiers_kept_ and _nullifiers_removed_ to _0_. - For each _nullifier_ at index _i_ in the _nullifier_contexts_ within the _private_inputs_, find the index of its corresponding _transient_nullifier_index_ at _`nullifier_index_hints[i]`_, provided as [hints](#hints-for-transient-note-reset-private-kernel-circuit): + For each _nullifier_ at index _i_ in the _nullifier_contexts_ within the _private_inputs_, find the index of its corresponding transient nullifier at _`nullifier_index_hints[i]`_, provided as [hints](#hints-for-transient-note-reset-private-kernel-circuit): - If _`nullifier_index_hints[i] == transient_nullifier_indices.len()`_: - Verify that the _nullifier_ remains within the _[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_ in the _public_inputs_: - - _`nullifier == public_inputs.transient_accumulated_data.nullifier_contexts[nullifiers_kept]`_ - - Increment _nullifiers_kept_ by 1: _`nullifiers_kept += 1`_ + _`nullifier == public_inputs.transient_accumulated_data.nullifier_contexts[nullifiers_kept]`_ + - Increment _nullifiers_kept_ by 1: _`nullifiers_kept += 1`_ - Else, compute _transient_nullifier_index_ as _`transient_nullifier_indices[nullifier_index_hints[i]]`_: - Verify that: _`transient_nullifier_index == i`_ - Increment _nullifiers_removed_ by 1: _`nullifiers_removed += 1`_ @@ -106,7 +104,36 @@ In the event that a pending note is nullified within the same transaction, both _`nullifiers_removed == notes_removed`_ -> Note that this reset process may not necessarily be applied to all transient note hashes and nullifiers 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. +3. Ensure that _encrypted_note_preimage_hashes_ not associated with note hashes removed in the previous step are retained within the _[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_ in the _public_inputs_. + + Initialize both _hashes_kept_ and _hashes_removed_ to _0_. + + For each _preimage_hash_ at index _i_ in the _encrypted_note_preimage_hash_contexts_ within the _private_inputs_, find the _index_hint_ of its corresponding hash within _public_inputs_ at _`encrypted_note_preimage_hash_index_hints[i]`_, provided as [hints](#hints-for-transient-note-reset-private-kernel-circuit): + + - If _`index_hint == encrypted_note_preimage_hash_contexts.len()`_: + - Ensure that the associated note hash is removed: + - Locate the _note_hash_ at _`private_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]`_. + - Verify that the _preimage_hash_ is associated with the _note_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()`_ + - 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 the _public_inputs_: + - _`encrypted_note_preimage_hash_contexts[N - hashes_removed].is_empty() == true`_ + - Where _N_ is the length of _encrypted_note_preimage_hash_contexts_. + - Else, find the _mapped_preimage_hash_ at _`encrypted_note_preimage_hash_contexts[index_hint]`_ within _public_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_ at _`public_inputs.transient_accumulated_data.note_hash_contexts[log_note_hash_hints[i]]`_. + - Verify that the _preimage_hash_ is associated with the _note_hash_: + - _`preimage_hash.note_hash_counter == note_hash.counter`_ + - _`preimage_hash.contract_address == note_hash.contract_address`_ + - Increment _hashes_kept_ by 1: _`hashes_kept += 1`_ + +> 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 @@ -174,10 +201,12 @@ The format aligns with the _[PreviousKernel](./private-kernel-inner.md#previousk ### _Hints_ for [Transient Note Reset Private Kernel Circuit](#transient-note-reset-private-kernel-circuit) -| Field | Type | Description | -| ----------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------- | -| _transient_nullifier_indices_ | [_field_; _C_] | Indices of the nullifiers for transient notes. _C_ equals the length of _note_hash_contexts_. | -| _nullifier_index_hints_ | [_field_; _C_] | Indices of the _transient_nullifier_indices_ for transient nullifiers. _C_ equals the length of _nullifier_contexts_. | +| Field | Type | Description | +| ------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| _transient_nullifier_indices_ | [_field_; _C_] | Indices of the nullifiers for transient notes. _C_ equals the length of _note_hash_contexts_. | +| _nullifier_index_hints_ | [_field_; _C_] | Indices of the _transient_nullifier_indices_ for transient nullifiers. _C_ equals the length of _nullifier_contexts_. | +| _encrypted_note_preimage_hash_index_hints_ | [_field_; _C_] | Indices of the _encrypted_note_preimage_hash_contexts_ for transient preimage hashes. _C_ equals the length of _encrypted_note_preimage_hash_contexts_. | +| _log_note_hash_hints_ | [_field_; _C_] | Indices of the _note_hash_contexts_ for transient preimage hashes. _C_ equals the length of _note_hash_contexts_. | ## Public Inputs diff --git a/yellow-paper/docs/circuits/private-kernel-tail.md b/yellow-paper/docs/circuits/private-kernel-tail.md index 572f23bd5f4..d75b1a34b04 100644 --- a/yellow-paper/docs/circuits/private-kernel-tail.md +++ b/yellow-paper/docs/circuits/private-kernel-tail.md @@ -30,7 +30,7 @@ It checks the data within _[private_inputs](#private-inputs).[previous_kernel](# - _read_requests_ - The _nullifier_counter_ associated with each note hash in _note_hash_contexts_. - - The _nullified_note_hash_ associated with each nullifier in _nullifier_contexts_. + - The _note_hash_counter_ associated with each nullifier in _nullifier_contexts_. > 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. @@ -42,7 +42,7 @@ Siloing a value with the address of the contract generating the value ensures th 1. Silo _nullifiers_: - For each _nullifier_ at index _i_ _> 0_ in the _nullifier_contexts_ within _private_inputs_, if _`nullifier.value != 0`_: + For each _nullifier_ at index _i_ **> 0** in the _nullifier_contexts_ within _private_inputs_, if _`nullifier.value != 0`_: _`nullifier_contexts[i].value = hash(nullifier.contract_address, nullifier.value)`_ @@ -58,7 +58,7 @@ Siloing a value with the address of the contract generating the value ensures th - _`nonce = hash(first_nullifier, index)`_ - _`first_nullifier = nullifier_contexts[0].value`_. - - _`index = note_hash_indices[i]`_, which is the index of the same note hash within _public_inputs.note_hashes_. Where _note_hash_indices_ is provided as [hints](#hints) via _private_inputs_ and are verified in the [following step](#verifying-ordered-arrays). + - _`index = note_hash_hints[i]`_, which is the index of the same note hash within _public_inputs.note_hashes_. Where _note_hash_hints_ is provided as [hints](#hints) via _private_inputs_. - _`siloed_hash = hash(note_hash.contract_address, note_hash.value)`_ > Siloing with a nonce guarantees that each final note hash is a unique value in the note hash tree. @@ -73,59 +73,67 @@ Siloing a value with the address of the contract generating the value ensures th - Verify that _`l2_to_l1_messages[i] == hash(l2_to_l1_message.contract_address, version_id, l2_to_l1_message.portal_contract_address, chain_id, l2_to_l1_message.value)`_ - Where _version_id_ and _chain_id_ are defined in _[public_inputs](#public-inputs).[constant_data](./private-kernel-initial.md#constantdata).[tx_context](./private-kernel-initial.md#transactioncontext)_. -#### Verifying ordered arrays. +4. Silo _unencrypted_log_hashes_: -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. + For each _log_hash_ at index _i_ in the _unencrypted_log_hash_contexts_ within _private_inputs_, if _`log_hash.hash != 0`_: -This circuit ensures the correct ordering of the following arrays within _[public_inputs](#public-inputs).[accumulated_data](./public-kernel-tail.md#accumulateddata)_: + _`unencrypted_log_hash_contexts[i].value = hash(log_hash.hash, log_hash.contract_address)`_ -- _note_hashes_ -- _nullifiers_ -- _public_call_requests_ +5. Silo _encrypted_log_hashes_: -The corresponding unordered array for each of the above is sourced from _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./private-kernel-initial.md#public-inputs).[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_. + For each _log_hash_ at index _i_ in the _encrypted_log_hash_contexts_ within _private_inputs_, if _`log_hash.hash != 0`_: -1. Verify ordered _note_hashes_: + _`encrypted_log_hash_contexts[i].value = hash(log_hash.hash, contract_address_tag)`_ - For each _note_hash_ at index _i_ in _note_hashes_, the associated _note_hash_context_ is at _`note_hash_contexts[note_hash_hints[i]]`_. + Where _`contract_address_tag = hash(log_hash.contract_address, log_hash.randomness)`_ - - If _`note_hash != 0`_, verify that: - - _`note_hash == note_hash_context.value`_ - - _`note_hash_indices[note_hash_hints[i]] == i`_ - - The values in _note_hash_indices_ are provided as [hints](#hints) to [silo note hashes](#siloing-values) and must be verified. - - If _i > 0_, verify that: - - _`note_hash_context.counter > note_hash_contexts[note_hash_hints[i - 1]].counter`_ - - Else: - - All the subsequent values in _note_hashes_ must be 0. - - All the subsequent contexts in _note_hash_contexts_ must have 0 values. +#### Verifying ordered arrays. -2. Verify ordered _nullifiers_: +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. - For each _nullifier_ at index _i_ in _nullifiers_, the associated _nullifier_context_ is at _`nullifier_contexts[nullifier_hints[i]]`_. +This circuit ensures the correct ordering of the following arrays: - - If _`nullifier != 0`_, verify that: - - _`nullifier == nullifier_context.value`_ - - If _i > 0_, verify that: - - _`nullifier_context.counter > nullifier_contexts[nullifier_hints[i - 1]].counter`_ - - Else: - - All the subsequent values in _nullifiers_ must be 0. - - All the subsequent contexts in _nullifier_contexts_ must have 0 values. +- _note_hashes_ +- _nullifiers_ +- _public_call_requests_ +- _ordered_unencrypted_log_hashes_ +- _ordered_encrypted_log_hashes_ +- _ordered_encrypted_note_preimage_hashes_ + +Where: -3. Verify ordered _public_call_requests_: +- _note_hashes_, _nullifiers_, and _public_call_requests_ are within _[public_inputs](#public-inputs).[accumulated_data](./public-kernel-tail.md#accumulateddata)_. +- _ordered_unencrypted_log_hashes_, _ordered_encrypted_log_hashes_, and _ordered_encrypted_note_preimage_hashes_ are provided as hints through _private_inputs_. +- Every corresponding unordered array for each of the ordered array is sourced from _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./private-kernel-initial.md#public-inputs).[transient_accumulated_data](./private-kernel-initial.md#transientaccumulateddata)_. - For each _request_ at index _i_ in _public_call_requests_ within _public_inputs_, the associated _mapped_request_ is at _`unordered_requests[public_call_request_hints[i]]`_, where _unordered_requests_ refers to the _public_call_requests_ within _private_inputs_. +1. Verify ordered _public_call_requests_: + + For each _request_ at index _i_ in _`private_inputs.previous_kernel.public_inputs.transient_accumulated_data.public_call_requests[i]`_, the associated _mapped_request_ is at _`public_call_requests[public_call_request_hints[i]]`_ within _public_inputs_. - If _`request.hash != 0`_, verify that: - _`request.hash == mapped_request.hash`_ - _`request.caller_contract == mapped_request.caller_contract`_ - _`request.caller_context == mapped_request.caller_context`_ - If _i > 0_, verify that: - - _`mapped_request.counter < unordered_requests[public_call_request_hints[i - 1]].counter`_ + - _`mapped_request[i].counter < mapped_request[i - 1].counter`_ - Else: - - All the subsequent requests in both _public_call_requests_ and _unordered_requests_ must have 0 hashes. + - All the subsequent requests (_index >= i_) in both _public_call_requests_ and _unordered_requests_ must be empty. > Note that _public_call_requests_ must be arranged in descending order to ensure the calls are executed in chronological order. +2. Verify the rest of the ordered arrays: + + For each _note_hash_context_ at index _i_ in the **unordered** _note_hash_contexts_ within _private_inputs_, the associated _note_hash_ is at _`note_hashes[note_hash_hints[i]]`_. + + - If _`note_hash != 0`_, verify that: + - _`note_hash == note_hash_context.value`_ + - If _i > 0_, verify that: + - _`note_hashes[i].counter > note_hashes[i - 1].counter`_ + - Else: + - All the subsequent items (_index >= i_) in both _note_hashes_ and _note_hash_contexts_ must be empty. + + Repeat the same process for _nullifiers_, _ordered_unencrypted_log_hashes_, _ordered_encrypted_log_hashes_, and _ordered_encrypted_note_preimage_hashes_. + > 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. @@ -155,12 +163,20 @@ The _counter_start_ in the _public_call_requests_ within _public_inputs_ should - _note_hashes_ - _nullifiers_ -3. The following must match the respective values within _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./private-kernel-initial.md#public-inputs).[accumulated_data](./private-kernel-initial.md#accumulateddata)_: +3. The hashes and lengths for all logs are accumulated as follows: + + For each non-empty _log_hash_ at index _i_ in _ordered_unencrypted_log_hashes_, which is provided as [hints](#hints), and the [ordering](#verifying-ordered-arrays) was verified against the [siloed hashes](#siloing-values) in previous steps: + + - _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash.hash)`_ + - If _i == 0_: _`accumulated_logs_hash = log_hash.hash`_ + - _`accumulated_logs_length += log_hash.length`_ + + Check the values in the _public_inputs_ are correct: + + - _`unencrypted_logs_hash == accumulated_logs_hash`_ + - _`unencrypted_log_preimages_length == accumulated_logs_length`_ - - _encrypted_logs_hash_ - - _unencrypted_logs_hash_ - - _encrypted_log_preimages_length_ - - _unencrypted_log_preimages_length_ + Repeat the same process for _encrypted_logs_hash_, _encrypted_log_preimages_length_, _encrypted_note_preimages_hash_ and _encrypted_note_preimages_length_. 4. The following must be empty: @@ -187,12 +203,17 @@ The format aligns with the _[PreviousKernel](./private-kernel-inner.md#previousk Data that aids in the verifications carried out in this circuit: -| Field | Type | Description | -| --------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- | -| _note_hash_indices_ | [_field_; _C_] | Indices of _note_hashes_ for _note_hash_contexts_. _C_ equals the length of _note_hashes_. | -| _note_hash_hints_ | [_field_; _C_] | Indices of _note_hash_contexts_ for ordered _note_hashes_. _C_ equals the length of _note_hash_contexts_. | -| _nullifier_hints_ | [_field_; _C_] | Indices of _nullifier_contexts_ for ordered _nullifiers_. _C_ equals the length of _nullifier_contexts_. | -| _public_call_request_hints_ | [_field_; _C_] | Indices of _public_call_requests_ for ordered _public_call_requests_. _C_ equals the length of _public_call_requests_. | +| Field | Type | Description | +| ---------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| _note_hash_hints_ | [_field_; _C_] | Indices of ordered _note_hashes_ for _note_hash_contexts_. _C_ equals the length of _note_hash_contexts_. | +| _nullifier_hints_ | [_field_; _C_] | Indices of ordered _nullifiers_ for _nullifier_contexts_. _C_ equals the length of _nullifier_contexts_. | +| _public_call_request_hints_ | [_field_; _C_] | Indices of ordered _public_call_requests_ for _public_call_requests_. _C_ equals the length of _public_call_requests_. | +| _ordered_unencrypted_log_hashes_ | [_field_; _C_] | Ordered _unencrypted_log_hashes_. _C_ equals the length of _unencrypted_log_hash_contexts_. | +| _unencrypted_log_hash_hints_ | [_field_; _C_] | Indices of _ordered_unencrypted_log_hashes_ for _unencrypted_log_hash_contexts_. _C_ equals the length of _unencrypted_log_hash_contexts_. | +| _ordered_encrypted_log_hashes_ | [_field_; _C_] | Ordered _encrypted_log_hashes_. _C_ equals the length of _encrypted_log_hash_contexts_. | +| _encrypted_log_hash_hints_ | [_field_; _C_] | Indices of _ordered_encrypted_log_hashes_ for _encrypted_log_hash_contexts_. _C_ equals the length of _encrypted_log_hash_contexts_. | +| _ordered_encrypted_note_preimage_hashes_ | [_field_; _C_] | Ordered _encrypted_note_preimage_hashes_. _C_ equals the length of _encrypted_note_preimage_hash_contexts_. | +| _encrypted_note_preimage_hints_ | [_field_; _C_] | Indices of _ordered_encrypted_note_preimage_hashes_ for _encrypted_note_preimage_hash_contexts_. _C_ equals the length of _encrypted_note_preimage_hash_contexts_. | ## Public Inputs diff --git a/yellow-paper/docs/circuits/public-kernel-inner.md b/yellow-paper/docs/circuits/public-kernel-inner.md index 5d7aaae0246..706c8949fe8 100644 --- a/yellow-paper/docs/circuits/public-kernel-inner.md +++ b/yellow-paper/docs/circuits/public-kernel-inner.md @@ -73,6 +73,7 @@ It ensures the public function's intention by checking the following in _[public - _nullifiers_ - _l2_to_l1_messages_ - _storage_writes_ + - _unencrypted_log_hashes_ #### Verifying the counters. @@ -113,20 +114,11 @@ It verifies that each value listed below is associated with a legitimate counter - _l2_to_l1_messages_ - _encrypted_logs_hash_ - _encrypted_log_preimages_length_ + - _encrypted_note_preimages_hash_ + - _encrypted_note_preimages_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: - - - _`unencrypted_logs_hash = hash(prev_hash, cur_hash)`_ - - If either hash is zero, the new hash will be `prev_hash | cur_hash` - - _`unencrypted_log_preimages_length = prev_length + cur_length`_ - - Where: - - - _prev_hash_ and _prev_length_ are the values in _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./public-kernel-tail.md#public-inputs).[accumulated_data](./public-kernel-tail.md#accumulateddata)_. - - _cur_hash_ and _cur_length_ are the values in _[private_inputs](#private-inputs).[public_call](#publiccall).[call_stack_item](#publiccallstackitem).[public_inputs](#publicfunctionpublicinputs)_. - #### Verifying the transient accumulated data. The _[transient_accumulated_data](./public-kernel-tail.md#transientaccumulateddata)_ in this circuit's _[public_inputs](#public-inputs)_ includes values from both the previous iterations and the _[public_call](#publiccall)_. @@ -151,6 +143,8 @@ For the subsequent items appended after the values from the previous iterations, - _value_, _counter_ - _storage_writes_ - _value_, _counter_ + - _unencrypted_log_hash_contexts_ + - _hash_, _length_, _counter_ 2. For _public_call_requests_: @@ -167,6 +161,7 @@ For the subsequent items appended after the values from the previous iterations, - _l2_to_l1_message_contexts_ - _storage_reads_ - _storage_writes_ + - _unencrypted_log_hash_contexts_ > Ensuring the alignment of the contract addresses is crucial, as it is later used to [silo the values](./public-kernel-tail.md#siloing-values) and to establish associations with values within the same contract. @@ -224,21 +219,20 @@ The format aligns with the _[Public Inputs](./public-kernel-tail.md#public-input ### _PublicFunctionPublicInputs_ -| Field | Type | Description | -| ---------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- | -| _call_context_ | _[CallContext](./private-function.md#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. | -| _note_hashes_ | [_[NoteHash](./private-function.md#notehash)_; _C_] | New note hashes created in this function call. | -| _nullifiers_ | [_[Nullifier](./private-function.md#nullifier)_; _C_] | New nullifiers created in this function call. | -| _l2_to_l1_messages_ | [_field_; _C_] | New L2 to L1 messages created in this function call. | -| _storage_reads_ | [_[StorageRead](./public-kernel-tail.md#storageread)_; _C_] | Data read from the public data tree. | -| _storage_writes_ | [_[StorageWrite](./public-kernel-tail.md#storagewrite)_; _C_] | Data written to the public data tree. | -| _unencrypted_logs_hash_ | _field_ | Hash of the unencrypted logs emitted in this function call. | -| _unencrypted_log_preimages_length_ | _field_ | Length of the unencrypted log preimages emitted in this function call. | -| _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | -| _block_header_ | _[BlockHeader](#blockheader)_ | Information about the trees used for the transaction. | -| _chain_id_ | _field_ | Chain ID of the transaction. | -| _version_ | _field_ | Version of the transaction. | +| Field | Type | Description | +| ------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------- | +| _call_context_ | _[CallContext](./private-function.md#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. | +| _note_hashes_ | [_[NoteHash](./private-function.md#notehash)_; _C_] | New note hashes created in this function call. | +| _nullifiers_ | [_[Nullifier](./private-function.md#nullifier)_; _C_] | New nullifiers created in this function call. | +| _l2_to_l1_messages_ | [_field_; _C_] | New L2 to L1 messages created in this function call. | +| _storage_reads_ | [_[StorageRead](./public-kernel-tail.md#storageread)_; _C_] | Data read from the public data tree. | +| _storage_writes_ | [_[StorageWrite](./public-kernel-tail.md#storagewrite)_; _C_] | Data written to the public data tree. | +| _unencrypted_log_hashes_ | [_[UnencryptedLogHash](./private-function.md#unencryptedloghash)_; _C_] | Hashes of the unencrypted logs emitted in this function call. | +| _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | +| _block_header_ | _[BlockHeader](#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. diff --git a/yellow-paper/docs/circuits/public-kernel-tail.md b/yellow-paper/docs/circuits/public-kernel-tail.md index 791bfba95cc..69631ea47c5 100644 --- a/yellow-paper/docs/circuits/public-kernel-tail.md +++ b/yellow-paper/docs/circuits/public-kernel-tail.md @@ -43,19 +43,24 @@ This circuit ensures the correct ordering of the following: - _nullifiers_ - _storage_reads_ - _storage_writes_ +- _ordered_unencrypted_log_hashes_ -1. For _note_hashes_ and _nullifiers_, they undergo the same [process](./private-kernel-tail.md#verifying-ordered-arrays) as outlined in the tail private kernel circuit. With the exception that the loop starts from index _offset + i_, where _offset_ is the number of non-zero values in the _note_hashes_ and _nullifiers_ arrays within _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./public-kernel-tail.md#public-inputs).[accumulated_data](./public-kernel-tail.md#accumulateddata)_. +1. For _note_hashes_, _nullifiers_, and _ordered_unencrypted_log_hashes_, they undergo the same [process](./private-kernel-tail.md#verifying-ordered-arrays) as outlined in the tail private kernel circuit. With the exception that the loop starts from index _offset + i_, where _offset_ is the number of non-zero values in the _note_hashes_ and _nullifiers_ arrays within _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./public-kernel-tail.md#public-inputs).[accumulated_data](./public-kernel-tail.md#accumulateddata)_. 2. For _storage_reads_, an _ordered_storage_reads_ and _storage_read_hints_ are provided as [hints](#hints) through _private_inputs_. This circuit checks that: - For each _read_ at index _i_ in _ordered_storage_reads_, the associated _mapped_read_ is at _`storage_reads[storage_read_hints[i]]`_. + For each _read_ at index _i_ in _`storage_reads[i]`_, the associated _mapped_read_ is at _`ordered_storage_reads[storage_read_hints[i]]`_. - If _`read.is_empty() == false`_, verify that: - - All values in _mapped_read_ align with those in _read_. + - All values in _read_ align with those in _mapped_read_: + - _`read.contract_address == mapped_read.contract_address`_ + - _`read.storage_slot == mapped_read.storage_slot`_ + - _`read.value == mapped_read.value`_ + - _`read.counter == mapped_read.counter`_ - If _i > 0_, verify that: - - _`read.counter > read[storage_read_hints[i - 1]].counter`_ + - _`mapped_read[i].counter > mapped_read[i - 1].counter`_ - Else: - - All the subsequent reads in both _storage_reads_ and _ordered_storage_reads_ must be empty. + - All the subsequent reads (_index >= i_) in both _storage_reads_ and _ordered_storage_reads_ must be empty. 3. For _storage_writes_, an _ordered_storage_writes_ and _storage_write_hints_ are provided as [hints](#hints) through _private_inputs_. The verification is the same as the process for _storage_reads_. @@ -215,12 +220,19 @@ After all the storage writes are processed: - _note_hashes_ - _nullifiers_ -3. The following must match the respective values within _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs](./private-kernel-initial.md#public-inputs).[accumulated_data](./private-kernel-initial.md#accumulateddata)_: +3. The hashes and lengths for unencrypted logs are accumulated as follows: - - _encrypted_logs_hash_ - - _unencrypted_logs_hash_ - - _encrypted_log_preimages_length_ - - _unencrypted_log_preimages_length_ + Initialize _accumulated_logs_hash_ to be the _unencrypted_logs_hash_ within _[private_inputs](#private-inputs).[previous_kernel](#previouskernel).[public_inputs].[accumulated_data](#accumulateddata)_. + + For each non-empty _log_hash_ at index _i_ in _ordered_unencrypted_log_hashes_, which is provided as [hints](#hints), and the [ordering](#verifying-ordered-arrays) was verified against the [siloed hashes](#siloing-values) in previous steps: + + - _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash.hash)`_ + - _`accumulated_logs_length += log_hash.length`_ + + Check the values in the _public_inputs_ are correct: + + - _`unencrypted_logs_hash == accumulated_logs_hash`_ + - _`unencrypted_log_preimages_length == accumulated_logs_length`_ 4. The following is referenced and verified in a [previous step](#updating-the-public-data-tree): @@ -241,7 +253,7 @@ This section follows the same [process](./private-kernel-inner.md#verifying-the- | Field | Type | Description | | -------------------- | -------------------------------------------------------------------- | -------------------------------------------- | -| _public_inputs_ | _[PrivateKernelPublicInputs](#public-inputs)_ | Public inputs of the proof. | +| _public_inputs_ | _[PublicKernelPublicInputs](#public-inputs)_ | Public inputs of the proof. | | _proof_ | _Proof_ | Proof of the kernel circuit. | | _vk_ | _VerificationKey_ | Verification key of the kernel circuit. | | _membership_witness_ | _[MembershipWitness](./private-kernel-initial.md#membershipwitness)_ | Membership witness for the verification key. | @@ -250,25 +262,27 @@ This section follows the same [process](./private-kernel-inner.md#verifying-the- Data that aids in the verifications carried out in this circuit: -| Field | Type | Description | -| ------------------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| _note_hash_indices_ | [_field_; _C_] | Indices of _note_hashes_ for _note_hash_contexts_. _C_ equals the length of _note_hashes_. | -| _note_hash_hints_ | [_field_; _C_] | Indices of _note_hash_contexts_ for ordered _note_hashes_. _C_ equals the length of _note_hash_contexts_. | -| _nullifier_hints_ | [_field_; _C_] | Indices of _nullifier_contexts_ for ordered _nullifiers_. _C_ equals the length of _nullifier_contexts_. | -| _ordered_storage_reads_ | [_[StorageReadContext](#storagereadcontext)_; _C_] | Ordered _storage_reads_. _C_ equals the length of _storage_reads_. | -| _storage_read_hints_ | [_field_; _C_] | Indices of reads for _ordered_storage_reads_. _C_ equals the length of _storage_reads_. | -| _ordered_storage_writes_ | [_[StorageWriteContext](#storagewritecontext)_; _C_] | Ordered _storage_writes_. _C_ equals the length of _storage_writes_. | -| _storage_write_hints_ | [_field_; _C_] | Indices of writes for _ordered_storage_writes_. _C_ equals the length of _storage_writes_. | -| _public_data_snaps_ | [_[PublicDataSnap](#publicdatasnap)_; _C_] | Data that aids verification of storage reads and writes. _C_ equals the length of _ordered_storage_writes_ + _ordered_storage_reads_. | -| _storage_write_indices_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for _public_data_snaps_. _C_ equals the length of _public_data_snaps_. | -| _transient_read_hints_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for transient reads. _C_ equals the length of _ordered_storage_reads_. | -| _persistent_read_hints_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for persistent reads. _C_ equals the length of _ordered_storage_reads_. | -| _public_data_snap_indices_ | [_field_; _C_] | Indices of _public_data_snaps_ for persistent write. _C_ equals the length of _ordered_storage_writes_. | -| _storage_read_low_leaf_preimages_ | [_[PublicDataLeafPreimage](#publicdataleafpreimage)_; _C_] | Preimages for public data leaf. _C_ equals the length of _ordered_storage_writes_. | -| _storage_read_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for persistent reads. _C_ equals the length of _ordered_storage_writes_. | -| _storage_write_low_leaf_preimages_ | [_[PublicDataLeafPreimage](#publicdataleafpreimage)_; _C_] | Preimages for public data. _C_ equals the length of _ordered_storage_writes_. | -| _storage_write_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for public data tree. _C_ equals the length of _ordered_storage_writes_. | -| _subtree_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for the public data subtree. _C_ equals the length of _ordered_storage_writes_. | +| Field | Type | Description | +| ------------------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| _note_hash_indices_ | [_field_; _C_] | Indices of _note_hashes_ for _note_hash_contexts_. _C_ equals the length of _note_hashes_. | +| _note_hash_hints_ | [_field_; _C_] | Indices of _note_hash_contexts_ for ordered _note_hashes_. _C_ equals the length of _note_hash_contexts_. | +| _nullifier_hints_ | [_field_; _C_] | Indices of _nullifier_contexts_ for ordered _nullifiers_. _C_ equals the length of _nullifier_contexts_. | +| _ordered_unencrypted_log_hashes_ | [_field_; _C_] | Ordered _unencrypted_log_hashes_. _C_ equals the length of _unencrypted_log_hashes_. | +| _unencrypted_log_hash_hints_ | [_field_; _C_] | Indices of _ordered_unencrypted_log_hashes_ for _unencrypted_log_hash_contexts_. _C_ equals the length of _unencrypted_log_hash_contexts_. | +| _ordered_storage_reads_ | [_[StorageReadContext](#storagereadcontext)_; _C_] | Ordered _storage_reads_. _C_ equals the length of _storage_reads_. | +| _storage_read_hints_ | [_field_; _C_] | Indices of reads for _ordered_storage_reads_. _C_ equals the length of _storage_reads_. | +| _ordered_storage_writes_ | [_[StorageWriteContext](#storagewritecontext)_; _C_] | Ordered _storage_writes_. _C_ equals the length of _storage_writes_. | +| _storage_write_hints_ | [_field_; _C_] | Indices of writes for _ordered_storage_writes_. _C_ equals the length of _storage_writes_. | +| _public_data_snaps_ | [_[PublicDataSnap](#publicdatasnap)_; _C_] | Data that aids verification of storage reads and writes. _C_ equals the length of _ordered_storage_writes_ + _ordered_storage_reads_. | +| _storage_write_indices_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for _public_data_snaps_. _C_ equals the length of _public_data_snaps_. | +| _transient_read_hints_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for transient reads. _C_ equals the length of _ordered_storage_reads_. | +| _persistent_read_hints_ | [_field_; _C_] | Indices of _ordered_storage_writes_ for persistent reads. _C_ equals the length of _ordered_storage_reads_. | +| _public_data_snap_indices_ | [_field_; _C_] | Indices of _public_data_snaps_ for persistent write. _C_ equals the length of _ordered_storage_writes_. | +| _storage_read_low_leaf_preimages_ | [_[PublicDataLeafPreimage](#publicdataleafpreimage)_; _C_] | Preimages for public data leaf. _C_ equals the length of _ordered_storage_writes_. | +| _storage_read_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for persistent reads. _C_ equals the length of _ordered_storage_writes_. | +| _storage_write_low_leaf_preimages_ | [_[PublicDataLeafPreimage](#publicdataleafpreimage)_; _C_] | Preimages for public data. _C_ equals the length of _ordered_storage_writes_. | +| _storage_write_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for public data tree. _C_ equals the length of _ordered_storage_writes_. | +| _subtree_membership_witnesses_ | [_[MembershipWitness](./private-kernel-initial.md#membershipwitness)_; _C_] | Membership witnesses for the public data subtree. _C_ equals the length of _ordered_storage_writes_. | ## Public Inputs @@ -285,10 +299,12 @@ Data accumulated during the execution of the transaction. | _note_hashes_ | [_field_; _C_] | Note hashes created in the transaction. | | _nullifiers_ | [_field_; _C_] | Nullifiers created in the transaction. | | _l2_to_l1_messages_ | [_field_; _C_] | L2-to-L1 messages created in the transaction. | -| _encrypted_logs_hash_ | _field_ | Hash of the accumulated encrypted logs. | | _unencrypted_logs_hash_ | _field_ | Hash of the accumulated unencrypted logs. | -| _encrypted_log_preimages_length_ | _field_ | Length of the accumulated encrypted log preimages. | -| _unencrypted_log_preimages_length_ | _field_ | Length of the accumulated unencrypted log preimages. | +| _unencrypted_log_preimages_length_ | _field_ | Length of all unencrypted log preimages. | +| _encrypted_logs_hash_ | _field_ | Hash of the accumulated encrypted logs. | +| _encrypted_log_preimages_length_ | _field_ | Length of all encrypted log preimages. | +| _encrypted_note_preimages_hash_ | _field_ | Hash of the accumulated encrypted note preimages. | +| _encrypted_note_preimages_length_ | _field_ | Length of all encrypted note preimages. | | _old_public_data_tree_snapshot_ | _[TreeSnapshot](#treesnapshot)_ | Snapshot of the public data tree prior to this transaction. | | _new_public_data_tree_snapshot_ | _[TreeSnapshot](#treesnapshot)_ | Snapshot of the public data tree after this transaction. | diff --git a/yellow-paper/docs/logs/index.md b/yellow-paper/docs/logs/index.md index 0df3029c11d..eeeaa07b9b5 100644 --- a/yellow-paper/docs/logs/index.md +++ b/yellow-paper/docs/logs/index.md @@ -2,49 +2,328 @@ title: Logs --- -Logs on Aztec are similar to logs on Ethereum and their goal is to allow smart contracts to communicate arbitrary data to the outside world. -Logs are events which are emitted during contract function execution. -Aztec protocol gives users the following assurances: +Logs on Aztec are similar to logs on Ethereum, serving the purpose of enabling smart contracts to convey arbitrary data to external entities. This communication occurs through three distinct types of logs: -1. The logs get published, -2. log integrity (the logs are not modified once emitted), -3. address of the source contract is verified to be correct (a contract can't impersonate another one). +- [Unencrypted log](#unencrypted-log). +- [Encrypted log](#encrypted-log). +- [Encrypted note preimage](#encrypted-note-preimage). -:::warning Expand on how this is ensured in circuits once [this discussion](https://forum.aztec.network/t/issues-with-logs/2609/) is wrapped up. -::: +These logs are generated in the course of contract function executions and play a pivotal role in aiding users to comprehend and leverage the received block data while facilitating interaction with the network. -# Types +## Requirements -There are 2 kinds of logs in Aztec protocol: unencrypted and encrypted. +1. **Availability**: The logs get published. -## Unencrypted + A rollup proof won't be accepted by the rollup contract if the log preimages are not available. Similarly, a sequencer cannot accept a transaction unless log preimages accompany the transaction data. -Unencrypted logs are used to communicate public information out of smart contracts. -Unencrypted logs can be emitted from both public and private functions. +2. **Immutability**: A log cannot be modified once emitted. -:::info -Emitting unencrypted logs from private functions can be a privacy leak but we decided to not forbid it in-protocol because it might allow for interesting usecases like custom encryption schemes using FHE etc. -::: + The protocol ensures that once a proof is generated at any stage (for a function, transaction, or block), the emitted logs are finalized. In other words, only the original log preimages can generate the committed hashes in the proof. Any party can use this attribute to verify that the provided log preimages are not tempered. + +3. **Integrity**: A contract cannot impersonate another contract. + + Every log is emitted by a specific contract, necessitating the identification of the contract address. This information is crucial for subsequent interactions with the contract or for interpreting the received data. The protocol ensures that the source contract's address for a log can be verified, while also preventing the forging of the addresses. + +## Log Hash + +### Hash Function + +The protocol uses **SHA256** as the hash function for logs, and then reduces the 256-bit result to 253 bits for representation as a field element. + +Throughout this page, `hash(value)` is an abbreviated form of: `truncate_to_field(SHA256(value))` + +### Hashing + +Regardless of the log type, the hash is derived from an array of fields, calculated as: + +`hash(log_preimage[0], log_preimage[1], ..., log_preimage[N - 1])` + +Here, _log_preimage_ is an array of field elements of length _N_, representing the data to be broadcasted. + +#### Emitting Logs from Function Circuits + +A function can emit an arbitrary number of logs, provided they don't exceed the specified [limit]. The function circuits must compute a hash for each log, and push all the hashes into the public inputs for further processing by the protocol circuits. + +#### Aggregation in Protocol Circuits + +To minimize the on-chain verification data size, protocol circuits aggregate log hashes. The end result is a single hash within the root rollup proof, encompassing all logs of the same type. + +Each protocol circuit outputs two values for each log type: + +- _`accumulated_logs_hash`_: A hash representing all logs. +- _`accumulated_logs_length`_: The total length of all log preimages. + +In cases where two proofs are combined to form a single proof, the _accumulated_logs_hash_ and _accumulated_logs_length_ from the two child proofs must be merged into one accumulated value: + +- _`accumulated_logs_hash = hash(proof_0.accumulated_logs_hash, proof_1.accumulated_logs_hash)`_ + - If either hash is zero, the new hash will be _`proof_0.accumulated_logs_hash | proof_1.accumulated_logs_hash`_. +- _`accumulated_logs_length = proof_0.accumulated_logs_length + proof_1.accumulated_logs_length`_ + +For private and public kernel circuits, beyond aggregating logs from a function call, they ensure that the contract's address emitting the logs is linked to the _logs_hash_. For more details, refer to the "Hashing" sections in [Unencrypted Log](#hashing-1), [Encrypted Log](#hashing-2), and [Encrypted Note Preimage](#hashing-3). + +### Encoding + +1. The encoded logs data of a transaction is a flatten array of all logs data within the transaction: + + _`tx_logs_data = [number_of_logs, ...log_data_0, ...log_data_1, ...]`_ + + The format of _log_data_ varies based on the log type. For specifics, see the "Encoding" sections in [Unencrypted Log](#encoding-1), [Encrypted Log](#encoding-2), and [Encrypted Note Preimage](#encoding-3). + +2. The encoded logs data of a block is a flatten array of a collection of the above _tx_logs_data_, with hints facilitating hashing replay in a binary tree structure: + + _`block_logs_data = [number_of_branches, number_of_transactions, ...tx_logs_data_0, ...tx_logs_data_1, ...]`_ + + - _number_of_transactions_ is the number of leaves in the left-most branch, restricted to either _1_ or _2_. + - _number_of_branches_ is the depth of the parent node of the left-most leaf. + +Here is a step-by-step example to construct the _block_logs_data_: + +1. A rollup, _R01_, merges two transactions: _tx0_ containing _tx_logs_data_0_, and _tx1_ containing _tx_logs_data_1_: + + ```mermaid + flowchart BT + tx0((tx0)) + tx1((tx1)) + R01((R01)) + tx0 --- R01 + tx1 --- R01 + ``` + + _block_logs_data_: _`[0, 2, ...tx_logs_data_0, ...tx_logs_data_1]`_ + + Where _0_ is the depth of the node _R01_, and _2_ is the number of aggregated _tx_logs_data_ of _R01_. + +2. Another rollup, _R23_, merges two transactions: _tx3_ containing _tx_logs_data_3_, and _tx2_ without any logs: + + ```mermaid + flowchart BT + tx2((tx2)) + tx3((tx3)) + R23((R23)) + tx2 -. no logs .- R23 + tx3 --- R23 + ``` + + _block_logs_data_: _`[0, 1, ...tx_logs_data_3]`_ + + Here, the number of aggregated _tx_logs_data_ is _1_. + +3. A rollup, _RA_, merges the two rollups _R01_ and _R23_: + + ```mermaid + flowchart BT + tx0((tx0)) + tx1((tx1)) + R01((R01)) + tx0 --- R01 + tx1 --- R01 + tx2((tx2)) + tx3((tx3)) + R23((R23)) + tx2 -.- R23 + tx3 --- R23 + RA((RA)) + R01 --- RA + R23 --- RA + ``` + + _block_logs_data_: _`[1, 2, ...tx_logs_data_0, ...tx_logs_data_1, 0, 1, ...tx_logs_data_3]`_ + + The result is the _block_logs_data_ of _R01_ concatenated with the _block_logs_data_ of _R23_, with the _number_of_branches_ of _R01_ incremented by _1_. The updated value of _number_of_branches_ (_0 + 1_) is also the depth of the node _R01_. + +4. A rollup, _RB_, merges the above rollup _RA_ and another rollup _R45_: + + ```mermaid + flowchart BT + tx0((tx0)) + tx1((tx1)) + R01((R01)) + tx0 --- R01 + tx1 --- R01 + tx2((tx2)) + tx3((tx3)) + R23((R23)) + tx2 -.- R23 + tx3 --- R23 + RA((RA)) + R01 --- RA + R23 --- RA + tx4((tx4)) + tx5((tx5)) + R45((R45)) + tx4 --- R45 + tx5 --- R45 + RB((RB)) + RA --- RB + R45 --- RB + ``` + + _block_logs_data_: _`[2, 2, ...tx_logs_data_0, ...tx_logs_data_1, 0, 1, ...tx_logs_data_3, 0, 2, ...tx_logs_data_4, ...tx_logs_data_5]`_ + + The result is the concatenation of the _block_logs_data_ from both rollups, with the _number_of_branches_ of the left-side rollup, _RA_, incremented by _1_. + +### Verification -## Encrypted +Upon receiving a proof and its encoded logs data, the entity can ensure the correctness of the provided _block_logs_data_ by verifying that the _accumulated_logs_hash_ in the proof can be derived from it: -Encrypted logs can be emitted only from private functions. -This is because to encrypt the log we need to get a secret and it's impossible to privately manage secrets in public domain. +```js +const accumulated_logs_hash = compute_accumulated_logs_hash(block_logs_data); +assert(accumulated_logs_hash == proof.accumulated_logs_hash); +assert(block_logs_data.accumulated_logs_length == proof.accumulated_logs_length); + +function compute_accumulated_logs_hash(logs_data) { + const number_of_branches = logs_data.read_u32(); + + const number_of_transactions = logs_data.read_u32(); + let res = hash_tx_logs_data(logs_data); + if number_of_transactions == 2 { + res = hash(res, hash_tx_logs_data(logs_data)); + } + + for (let i = 0; i < number_of_branches; ++i) { + const res_right = compute_accumulated_logs_hash(logs_data); + res = hash(res, res_right); + } + + return res; +} + +function hash_tx_logs_data(logs_data) { + const number_of_logs = logs_data.read_u32(); + let res = hash_log_data(logs_data); + for (let i = 1; i < number_of_logs; ++i) { + const log_hash = hash_log_data(logs_data); + res = hash(res, log_hash); + } + return res; +} +``` + +The _accumulated_logs_length_ in _block_logs_data_ is computed during the processing of each _logs_data_ within _hash_log_data()_. The implementation of _hash_log_data_ varies depending on the type of the logs being processed. Refer to the "Verification" sections in [Unencrypted Log](#verification-1), [Encrypted Log](#verification-2), and [Encrypted Note Preimage](#verification-3) for details. + +## Unencrypted Log + +Unencrypted logs are used to communicate public information out of smart contracts. They can be emitted from both public and private functions. :::info -An important usecase of encrypted logs is delivery of notes (note commitment/hash preimage) to recipients. +Emitting unencrypted logs from private functions may pose a privacy leak. However, in-protocol restrictions are intentionally omitted to allow for potentially valuable use cases, such as custom encryption schemes utilizing Fully Homomorphic Encryption (FHE), and similar scenarios. ::: -### Log encryption +### Hashing -:::warning -Expand here how exactly the logs are encrypted. -I (benesjan) am not up-to-date on what is the encryption end-game. -::: +Following the iterations for all private or public calls, the tail kernel circuits hash each log hash with the contract contract before computing the _accumulated_logs_hash_. -# Encoding +1. Hash the _contract_address_ to each _log_hash_: -Just like on Ethereum, logs are ABI encoded. + - _`log_hash_a = hash(log_hash_a, contract_address_a)`_ + - Repeat the process for all _log_hashes_ in the transaction. -:::warning As far as I know the encoding will be happening in app circuit and won't be enforced by protocol. Should this section not be here for this reason? -::: +2. Accumulate all the hashes and output the final hash to the public inputs: + + - _`accumulated_logs_hash = hash(logs_hash_a, logs_hash_b)`_ + - For tail public kernel circuit, it begins with _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_a)`_ if the _accumulated_logs_hash_ outputted from the tail private kernel circuit is not empty. + - _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_c)`_ + - Repeat the process until all _logs_hashes_ are collectively hashed. + +### Encoding + +The following represents the encoded data for an unencrypted log: + +_`log_data = [log_preimage_length, contract_address, ...log_preimage]`_ + +### Verification + +```js +function hash_log_data(logs_data) { + const log_preimage_length = logs_data.read_u32(); + logs_data.accumulated_logs_length += log_preimage_length; + const contract_address = logs_data.read_field(); + const log_preimage = logs_data.read_fields(log_preimage_length); + const log_hash = hash(...log_preimage); + return hash(log_hash, contract_address); +} +``` + +## Encrypted Log + +Encrypted logs contain information encrypted using the recipient's key. They can only be emitted from private functions. This restriction is due to the necessity of obtaining a secret for log encryption, which is challenging to manage privately in a public domain. + +### Hashing + +Private kernel circuits ensure the association of the contract address with each encrypted _log_hash_. However, unlike unencrypted logs, submitting encrypted log preimages with their contract address poses a significant privacy risk. Therefore, instead of using the _contract_address_, a _contract_address_tag_ is generated for each encrypted _log_hash_. + +The _contract_address_tag_ is a hash of the _contract_address_ and a random value _randomness_, computed as: + +_`contract_address_tag = hash(contract_address, randomness)`_. + +Here, _randomness_ is generated in the private function circuit and supplied to the private kernel circuit. The value must be included in the preimage for encrypted log generation. The private function circuit is responsible for ensuring that the _randomness_ differs for every encrypted log to avoid potential information linkage based on identical _contract_address_tag_. + +After successfully decrypting an encrypted log, one can use the _randomness_ in the log preimage, hash it with the _contract_address_, and verify it against the _contract_address_tag_ to ascertain that the log originated from the specified contract. + +1. Hash the _contract_address_tag_ to each _log_hash_: + + - _`contract_address_tag_a = hash(contract_address_a, randomness)`_ + - _`log_hash_a = hash(log_hash_a, contract_address_tag_a)`_ + - Repeat the process for all _log_hashes_ in the transaction. + +2. Accumulate all the hashes and outputs the final hash to the public inputs: + + - _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_ + - _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_ + - Repeat the process until all _logs_hashes_ are collectively hashed. + +### Encoding + +The following represents the encoded data for an unencrypted log: + +_`log_data = [log_preimage_length, contract_address_tag, ...log_preimage]`_ + +### Verification + +```js +function hash_log_data(logs_data) { + const log_preimage_length = logs_data.read_u32(); + logs_data.accumulated_logs_length += log_preimage_length; + const contract_address_tag = logs_data.read_field(); + const log_preimage = logs_data.read_fields(log_preimage_length); + const log_hash = hash(...log_preimage); + return hash(log_hash, contract_address_tag); +} +``` + +## Encrypted Note Preimage + +Similar to [encrypted logs](#encrypted-log), encrypted note preimages are data that only entities possessing the keys can decrypt to view the plaintext. Unlike encrypted logs, each encrypted note preimage can be linked to a note, whose note hash can be found in the block data. + +> Note that a note can be "shared" to one or more recipients by emitting one or more encrypted note preimages. However, this is not mandatory, and there may be no encrypted preimages emitted for a note if the information can be obtain through alternative means. + +### Hashing + +As each encrypted note preimage can be associated with a note in the same transaction, enforcing a _contract_address_tag_ is unnecessary. Instead, by calculating the _note_hash_ using the decrypted note preimage, hashed with the _contract_address_, and verify it against the block data, the recipient can confirm that the note was emitted from the specified contract. + +The kernel circuit simply accumulates all the hashes: + +- _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_ +- _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_ +- Repeat the process until all _logs_hashes_ are collectively hashed. + +### Encoding + +The following represents the encoded data for an unencrypted note preimage: + +_`log_data = [log_preimage_length, ...log_preimage]`_ + +### Verification + +```js +function hash_log_data(logs_data) { + const log_preimage_length = logs_data.read_u32(); + logs_data.accumulated_logs_length += log_preimage_length; + const log_preimage = logs_data.read_fields(log_preimage_length); + return hash(...log_preimage); +} +``` + +## Log Encryption + +Refer to [Private Message Delivery](../private-message-delivery/) for detailed information on generating encrypted data.