Skip to main content

Public Kernel Circuit - Inner

danger

The public kernel circuits are being redesigned to accommodate the latest AVM designs. This page is therefore highly likely to change significantly.

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, sourced from private_inputs.previous_kernel.

The preceding proof can be:

Processing Public Function Call

Ensuring the function being called exists in the contract.

This section follows the same process as outlined in the initial private kernel circuit.

Ensuring the contract instance being called is deployed.

It verifies the public deployment of the contract instance by conducting a membership proof, where:

Ensuring the current call matches the call request.

The top item in the public_call_requests of the previous_kernel must pertain to the current function call.

This circuit will:

  1. Pop the request from the stack:

    • call_request = previous_kernel.public_inputs.transient_accumulated_data.public_call_requests.pop()
  2. Compare the hash with that of the current function call:

    • call_request.hash == public_call.call_stack_item.hash()
    • The hash of the call_stack_item is computed as:
      • hash(contract_address, function_data.hash(), public_inputs.hash(), counter_start, counter_end)
      • Where function_data.hash() and public_inputs.hash() are the hashes of the serialized field elements.

Ensuring this function is called with the correct context.

This section follows the same process as outlined in the inner private kernel circuit.

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 public function's intention by checking the following in public_call.call_stack_item.public_inputs:

  • The header must match the one in the constant_data.
  • If it is a static call (public_inputs.call_context.is_static_call == true), it ensures that the function does not induce any state changes by verifying that the following arrays are empty:
    • note_hashes
    • nullifiers
    • l2_to_l1_messages
    • storage_writes
    • unencrypted_log_hashes

Verifying the counters.

It verifies that each value listed below is associated with a legitimate counter.

  1. For the call_stack_item:

    • The counter_start and counter_end must match those in the call_request popped from the public_call_requests in a previous step.
  2. For items in each ordered array in call_stack_item.public_inputs:

    • The counter of the first item must be greater than the counter_start of the current call.
    • The counter of each subsequent item must be greater than the counter of the previous item.
    • The counter of the last item must be less than the counter_end of the current call.

    The ordered arrays include:

    • storage_reads
    • storage_writes
  3. For the last N non-empty requests in public_call_requests within public_inputs.transient_accumulated_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 call_stack_item.

    • 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 call_stack_item.

    N is the number of non-zero hashes in the public_call_stack_item_hashes in private_inputs.public_call.public_inputs.

Validating Public Inputs

Verifying the accumulated data.

  1. It verifies that the following in the accumulated_data align with their corresponding values in public_call.call_stack_item.public_inputs.

    • note_hashes
    • nullifiers
    • 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

Verifying the transient accumulated data.

The transient_accumulated_data in this circuit's public_inputs_ includes values from both the previous iterations and the public_call.

For each array in the transient_accumulated_data, this circuit verifies that it is populated with the values from the previous iterations, specifically:

  • public_inputs.transient_accumulated_data.ARRAY[0..N] == private_inputs.previous_kernel.public_inputs.transient_accumulated_data.ARRAY[0..N]

It's important to note that the top item in the public_call_requests from the previous_kernel won't be included, as it has been removed in a previous step.

For the subsequent items appended after the values from the previous iterations, they constitute the values from private_inputs.public_call.call_stack_item.public_inputs (public_function_public_inputs), and must undergo the following verifications:

  1. Ensure that the specified values in the following arrays match those in the corresponding arrays in the public_function_public_inputs:

    • note_hash_contexts
      • value, counter
    • nullifier_contexts
      • value, counter
    • l2_to_l1_message_contexts
      • value
    • storage_reads
      • value, counter
    • storage_writes
      • value, counter
    • unencrypted_log_hash_contexts
      • hash, length, counter
  2. For public_call_requests:

    • The hashes align with the values in the public_call_stack_item_hashes within public_function_public_inputs, but in reverse order.

    • The caller_contract_address equals the contract_address in public_call.call_stack_item.

    • The caller_context aligns with the values in the call_context within public_function_public_inputs.

    It's important that the call requests are arranged in reverse order to ensure they are executed in chronological order.

  3. The contract_address for each non-empty item in the following arrays must equal the storage_contract_address defined in public_function_public_inputs.call_context:

    • note_hash_contexts

    • nullifier_contexts

    • 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 and to establish associations with values within the same contract.

  4. The portal_contract_address for each non-empty item in l2_to_l1_message_contexts must equal the portal_contract_address defined in public_function_public_inputs.call_context.

  5. For each storage_write in storage_writes, verify that it is associated with an override_counter. The value of the override_counter can be:

    • Zero: if the storage_slot does not change later in the same transaction.

    • Greater than storage_write.counter: if the storage_slot is written again later in the same transaction.

    Override counters are used in the tail public kernel circuit to ensure a read happens before the value is changed in a subsequent write.

    Zero serves as an indicator for an unchanged update, as this value can never act as the counter of a write.

Verifying the constant data.

This section follows the same process as outlined in the inner private kernel circuit.

PrivateInputs

PreviousKernel

The format aligns with the PreviousKernel of the tail public kernel circuit.

PublicCall

Data that holds details about the current public function call.

FieldTypeDescription
call_stack_itemPublicCallStackItemInformation about the current public function call.
proofProofProof of the public function circuit.
vkVerificationKeyVerification key of the public function circuit.
bytecode_hashfieldHash of the function bytecode.
contract_dataContractInstanceData of the contract instance being called.
contract_class_dataContractClassData of the contract class.
function_leaf_membership_witnessMembershipWitnessMembership witness for the function being called.
contract_deployment_membership_witnessMembershipWitnessMembership witness for the deployment of the contract being called.

PublicInputs

The format aligns with the PublicInputs of the tail public kernel circuit.

Types

PublicCallStackItem

FieldTypeDescription
contract_addressAztecAddressAddress of the contract on which the function is invoked.
function_dataFunctionDataData of the function being called.
public_inputsPublicFunctionPublicInputsPublic inputs of the public vm circuit.
counter_startfieldCounter at which the function call was initiated.
counter_endfieldCounter at which the function call ended.

PublicFunctionPublicInputs

FieldTypeDescription
call_contextCallContextContext of the call corresponding to this function execution.
args_hashfieldHash of the function arguments.
return_values[field; C]Return values of this function call.
note_hashes[NoteHash; C]New note hashes created in this function call.
nullifiers[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_; C]Data read from the public data tree.
storage_writes[StorageWrite; C]Data written to the public data tree.
unencrypted_log_hashes[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.
headerHeaderInformation about the trees used for the transaction.
chain_idfieldChain ID of the transaction.
versionfieldVersion of the transaction.

The above Cs represent constants defined by the protocol. Each C might have a different value from the others.