Validium Docs
  • Overview
  • Connect to Validium
  • Start Coding 🚀
    • Quickstart
      • Overview
      • Deploy using Validium CLI
      • Deploy using Quickstart Repository
      • Deploy your first contract
      • Create an ERC20 token
  • Tooling
    • Block Explorers
    • Hardhat-Validium
      • Overview
      • Installation
      • Guides
        • Getting started
        • Migrating Hardhat project to Validium
        • Compiling non-inlinable libraries
      • Plugins
        • hardhat-zksync
        • hardhat-zksync-solc
        • hardhat-zksync-vyper
        • hardhat-zksync-deploy
        • hardhat-zksync-upgradable
        • hardhat-zksync-verify
        • hardhat-zksync-verify-vyper
        • hardhat-zksync-ethers
        • hardhat-zksync-node
        • Hardhat Community Plugins
    • Foundary
      • Overview
      • Installation
      • Getting Started
      • Migration Guide
        • Overview
        • Compilation
        • Deployment
        • Testing
  • Test and Debug
    • Getting Started
    • Docker L1 - L2 Nodes
    • In-Memory Node
    • Continuous Integration
    • Hardhat
    • Foundry
  • API Reference
    • Overview
    • Conventions
    • ZKs JSON-RPC API
    • Debug JSON-RPC API
    • Ethereum JSON-RPC API
    • PubSub JSON-RPC API
  • Concepts
    • Transaction Lifecycle
    • Blocks and Batches
    • Validium Network Fee Mechanism
    • Finality
    • System Upgrades
    • ZK Chains
    • Data Availability
      • Overview
      • Recreating L2 state from L1 pubdata
      • Validiums
    • Account Abstraction
    • L1 <-> L2 Communication
  • Components
    • Overview
    • Smart & System Contracts
      • Smart Contracts
      • System Contracts
    • Shared Bridges
    • Sequencer / Server
    • Validium Network EVM
      • Overview
      • Bootloader
      • Precompiles
      • Virtual Machine Specification
        • ZKsync Virtual Machine primer
        • VM Formal Specification
    • Prover
      • Overview
      • ZK Terminology
      • Running the Prover
      • Circuits
        • Overview
        • Circuit Testing
        • CodeDecommitter
        • DemuxLogQueue
        • ECRecover
        • KeccakRoundFunction
        • L1MessagesHasher
        • LogSorter
        • Main VM
        • RAMPermutation
        • Sha256RoundFunction
        • StorageApplication
        • Sorting and Deduplicating
          • Overview
          • SortDecommitments
          • StorageSorter
          • LogSorter
      • Boojum Gadgets
      • Boojum Function - `check_if_satisfied`
    • Compiler
      • Compiler Toolchain Overview
        • Compiler Toolchain Overview
        • Solidity Compiler
        • Vyper Compiler
        • LLVM Framework
      • Specification
        • Overview
        • Code Separation
        • System Contracts
        • Exception Handling
        • EVM Legacy Assembly Translator
        • Instructions
          • Instruction Reference
          • EVM
            • Native EVM Instructions
            • Arithmetic
            • Bitwise
            • Block
            • Call
            • Create
            • Environment
            • Logging
            • Logical
            • Memory
            • Return
            • Sha3
            • Stack
          • Extensions
            • Overview
            • Validium Network Extension Simulation (call)
            • Validium Network Extension Simulation (verbatim)
          • EVM Legacy Assembly
          • Yul
        • EraVM Binary Layout
    • Fee Withdrawer
    • Portal - Wallet + Bridge
    • Block Explorer
    • Transaction filtering
Powered by GitBook
On this page
  • KeccakRoundFunction PI
  • Input
  • Output
  • FSM Input and FSM Output
  • Main circuit logic
  • First part
  • Main part
  • Final part
  1. Components
  2. Prover
  3. Circuits

KeccakRoundFunction

PreviousECRecoverNextL1MessagesHasher

Last updated 8 months ago


KeccakRoundFunction PI

Input

pub struct PrecompileFunctionInputData<F: SmallField> {
    pub initial_log_queue_state: QueueState<F, QUEUE_STATE_WIDTH>,
    pub initial_memory_queue_state: QueueState<F, FULL_SPONGE_QUEUE_STATE_WIDTH>,
}

Output

pub struct PrecompileFunctionOutputData<F: SmallField> {
    pub final_memory_state: QueueState<F, FULL_SPONGE_QUEUE_STATE_WIDTH>,
}

FSM Input and FSM Output

pub struct Keccak256RoundFunctionFSMInputOutput<F: SmallField> {
    pub internal_fsm: Keccak256RoundFunctionFSM<F>,
    pub log_queue_state: QueueState<F, QUEUE_STATE_WIDTH>,
    pub memory_queue_state: QueueState<F, FULL_SPONGE_QUEUE_STATE_WIDTH>,
}

pub struct Keccak256RoundFunctionFSM<F: SmallField> {
    pub read_precompile_call: Boolean<F>,
    pub read_unaligned_words_for_round: Boolean<F>,
    pub completed: Boolean<F>,
    pub keccak_internal_state: [[[UInt8<F>; BYTES_PER_WORD]; LANE_WIDTH]; LANE_WIDTH],
    pub timestamp_to_use_for_read: UInt32<F>,
    pub timestamp_to_use_for_write: UInt32<F>,
    pub precompile_call_params: Keccak256PrecompileCallParams<F>,
    pub u8_words_buffer: [UInt8<F>; BYTES_BUFFER_SIZE],
    pub u64_words_buffer_markers: [Boolean<F>; BUFFER_SIZE_IN_U64_WORDS],
}

Main circuit logic

Keccak is a precompile for the keccak hash function, and is responsible for hashing any input data sent in by contract executions. Roughly speaking, the keccak circuit will receive metadata about queued up precompile calls, and ensure that the first-in-line call is indeed a call to the keccak precompile. The circuit then collects some metadata about the call itself, which tells the circuit at which memory position the input can be found, and at which memory position the output should be written, along with some peripheral data like the timestamp of the hash.

Next, the circuit will take data from another queue, which contains memory queries. This will give the circuit witnesses to push into the keccak buffer.

First part

The circuit begins with allocating input part of the PI.

let Keccak256RoundFunctionCircuitInstanceWitness {
    closed_form_input,
    requests_queue_witness,
    memory_reads_witness,
} = witness;

let mut structured_input = Keccak256RoundFunctionCircuitInputOutput::alloc_ignoring_outputs(
    cs,
    closed_form_input.clone(),
);

We chose what memory_queue state and log_queue state to continue to work with.

let requests_queue_state = QueueState::conditionally_select(
    cs,
    start_flag,
    &requests_queue_state_from_input,
    &requests_queue_state_from_fsm,
);

let memory_queue_state = QueueState::conditionally_select(
    cs,
    start_flag,
    &memory_queue_state_from_input,
    &memory_queue_state_from_fsm,
);

We do the same with inner FSM part.

let initial_state = Keccak256RoundFunctionFSM::conditionally_select(
    cs,
    start_flag,
    &starting_fsm_state,
    &structured_input.hidden_fsm_input.internal_fsm,
);

Main part

Our main cycle starts with getting a new precompile request from the queue.

let (precompile_call, _) = precompile_calls_queue.pop_front(cs, state.read_precompile_call);

We check that fields are correct.

Num::conditionally_enforce_equal(
    cs,
    state.read_precompile_call,
    &Num::from_variable(precompile_call.aux_byte.get_variable()),
    &Num::from_variable(aux_byte_for_precompile.get_variable()),
);
for (a, b) in precompile_call
    .address
    .inner
    .iter()
    .zip(precompile_address.inner.iter())
{
    Num::conditionally_enforce_equal(
        cs,
        state.read_precompile_call,
        &Num::from_variable(a.get_variable()),
        &Num::from_variable(b.get_variable()),
    );
}

Also, we prepare some additional information for the call.

state.precompile_call_params = Keccak256PrecompileCallParams::conditionally_select(
    cs,
    state.read_precompile_call,
    &call_params,
    &state.precompile_call_params,
);
...

Then we do some memory queries to read data that needed to be hashed.

let read_query = MemoryQuery {
    timestamp: state.timestamp_to_use_for_read,
    memory_page: state.precompile_call_params.input_page,
    index: state.precompile_call_params.input_offset,
    rw_flag: boolean_false,
    is_ptr: boolean_false,
    value: read_query_value,
};

memory_queue.push(cs, read_query, should_read);

After some another preparations, we are ready to create a full input.

let mut input = [zero_u8; keccak256::KECCAK_RATE_BYTES];
    input.copy_from_slice(&state.u8_words_buffer[..keccak256::KECCAK_RATE_BYTES]);

And run the round function.

let squeezed =
    keccak256_absorb_and_run_permutation(cs, &mut state.keccak_internal_state, &input);

Now, if it was the last round, we can make a write memory query of the result.

let write_query = MemoryQuery {
    timestamp: state.timestamp_to_use_for_write,
    memory_page: state.precompile_call_params.output_page,
    index: state.precompile_call_params.output_offset,
    rw_flag: boolean_true,
    is_ptr: boolean_false,
    value: result,
};

memory_queue.push(cs, write_query, write_result);

Final part

Now we update PI output parts and compute a commitment. Then we allocate it as public variables.

let compact_form =
    ClosedFormInputCompactForm::from_full_form(cs, &structured_input, round_function);
let input_commitment = commit_variable_length_encodable_item(cs, &compact_form, round_function);
for el in input_commitment.iter() {
    let gate = PublicInputGate::new(el.get_variable());
    gate.add_to_cs(cs);
}

Learn more about Keccak here: .

GitHub
GitHub
GitHub
https://keccak.team/keccak.html
GitHub
GitHub
GitHub