Skip to main content

Private Kernel Circuit - Inner

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 provided proof data, verification key, and public inputs, sourced from private_inputs.previous_kernel.

The preceding proof can be:

The previous proof and the proof for the current function call are verified using recursion.

Processing Private 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 current call matches the call request.

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

This circuit will:

  1. Pop the call request from the stack:

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

    • call_request.call_stack_item_hash == private_call.call_stack_item.hash()
    • The hash of the call_stack_item is computed as:
      • hash(contract_address, function_data.hash(), public_inputs.hash())
      • 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.

For the call_context in the public_inputs of the private_call.call_stack_item and the call_request popped in the previous step, this circuit checks that:

  1. If it is a standard call: call_context.is_delegate_call == false

    • The msg_sender of the current iteration must be the same as the caller's contract_address:
      • call_context.msg_sender == call_request.caller_contract_address
    • The storage_contract_address of the current iteration must be the same as its contract_address:
      • call_context.storage_contract_address == call_stack_item.contract_address
  2. If it is a delegate call: call_context.is_delegate_call == true

    • The caller_context in the call_request must not be empty. Specifically, the following values of the caller must not be zeros:
      • msg_sender
      • storage_contract_address
    • The msg_sender of the current iteration must equal the caller's msg_sender:
      • call_context.msg_sender == caller_context.msg_sender
    • The storage_contract_address of the current iteration must equal the caller's storage_contract_address:
      • call_context.storage_contract_address == caller_context.storage_contract_address
    • The storage_contract_address of the current iteration must not equal the contract_address:
      • call_context.storage_contract_address != call_stack_item.contract_address
  3. If it is NOT a static call: call_context.is_static_call == false

    • The previous iteration must not be a static call:
      • caller_context.is_static_call == false

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

This circuit verifies this proof and the proof of the previous kernel iteration 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 in private_call.call_stack_item.public_inputs:

  • The block_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
    • unencrypted_log_hashes
    • encrypted_log_hashes
    • encrypted_note_preimage_hashes

Verifying the counters.

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

Additionally, it verifies that for the call_stack_item, the counter_start and counter_end must match those in the call_request popped from the private_call_request_stack in a previous step.

Validating Public Inputs

Verifying the transient accumulated data.

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

For each array in the transient_accumulated_data, this circuit verifies that:

  1. 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 private_call_request_stack from the previous_kernel won't be included, as it has been removed in a previous step.

  2. As for the subsequent items appended after the values from the previous iterations, they constitute the values from the private_call, and each must undergo the same verification as outlined in the initial private kernel circuit.

Verifying other data.

It verifies that the constant_data and the min_revertible_side_effect_counter in the public_inputs align with the corresponding values in private_inputs.previous_kernel.public_inputs.

Private Inputs

PreviousKernel

Data of the previous kernel iteration.

FieldTypeDescription
public_inputsInitialPrivateKernelPublicInputsPublic inputs of the proof.
proofProofProof of the kernel circuit.
vkVerificationKeyVerification key of the kernel circuit.
membership_witnessMembershipWitnessMembership witness for the verification key.

PrivateCall

The format aligns with the PrivateCall of the initial private kernel circuit.

PublicInputs

The format aligns with the Public Inputs of the initial private kernel circuit.