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.