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
  • StorageApplication PI
  • Input
  • Output
  • FSM Input and FSM Output
  • Main circuit logic
  • First part
  • Main part
  • Final part
  1. Components
  2. Prover
  3. Circuits

StorageApplication

PreviousSha256RoundFunctionNextSorting and Deduplicating

Last updated 8 months ago


StorageApplication PI

Input

pub struct StorageApplicationInputData<F: SmallField> {
    pub shard: UInt8<F>,
    pub initial_root_hash: [UInt8<F>; 32],
    pub initial_next_enumeration_counter: [UInt32<F>; 2],
    pub storage_application_log_state: QueueState<F, QUEUE_STATE_WIDTH>,
}

Output

pub struct StorageApplicationOutputData<F: SmallField> {
    pub new_root_hash: [UInt8<F>; 32],
    pub new_next_enumeration_counter: [UInt32<F>; 2],
    pub state_diffs_keccak256_hash: [UInt8<F>; 32],
}

FSM Input and FSM Output

pub struct StorageApplicationFSMInputOutput<F: SmallField> {
    pub current_root_hash: [UInt8<F>; 32],
    pub next_enumeration_counter: [UInt32<F>; 2],
    pub current_storage_application_log_state: QueueState<F, QUEUE_STATE_WIDTH>,
    pub current_diffs_keccak_accumulator_state:
        [[[UInt8<F>; keccak256::BYTES_PER_WORD]; keccak256::LANE_WIDTH]; keccak256::LANE_WIDTH],
}

Main circuit logic

This circuit takes storage requests from storage_application_log_state. Then for each query, it verifies the read value and updates the root_hash is needed. Also, it outputs the hash of storage diffs. Shard_id if enforces to be 0 for now, because we have only one shard.

First part

The circuit begins with allocating input part of the PI.

let StorageApplicationCircuitInstanceWitness {
    closed_form_input,
    storage_queue_witness,
    merkle_paths,
    leaf_indexes_for_reads,
} = witness;

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

We chose what storage_application_log_state, root_hash and other fields to continue to work with.

let mut current_root_hash = UInt8::<F>::parallel_select(
    cs,
    start_flag,
    &structured_input.observable_input.initial_root_hash,
    &structured_input.hidden_fsm_input.current_root_hash,
);

let storage_accesses_queue_state = QueueState::conditionally_select(
    cs,
    start_flag,
    &storage_queue_state_from_input,
    &storage_queue_state_from_fsm,
);

...

Main part

Here’s the part, where all the main logic is implemented. Firstly, we take a new storage request if needed.

let (storage_log, _) = storage_accesses_queue.pop_front(cs, parse_next_queue_elem);

Now we can parse it and do some checks.

let LogQuery {
    address,
    key,
    read_value,
    written_value,
    rw_flag,
    shard_id,
    ..
} = storage_log;

We need a merkle path for executing query.

for _ in 0..STORAGE_DEPTH {
    let wit = merkle_path_witness_allocator.conditionally_allocate_biased(
        cs,
        parse_next_queue_elem,
        bias_variable,
    );
    bias_variable = wit.inner[0].get_variable();
    new_merkle_path_witness.push(wit);
}

Also, we update state_diffs data.

state_diff_data.address = UInt8::parallel_select(
    cs,
    parse_next_queue_elem,
    &address_bytes,
    &state_diff_data.address,
);
state_diff_data.key =
    UInt8::parallel_select(cs, parse_next_queue_elem, &key_bytes, &state_diff_data.key);
state_diff_data.derived_key = UInt8::parallel_select(
    cs,
    parse_next_queue_elem,
    &derived_key,
    &state_diff_data.derived_key,
);
...

Finally, we compute a new merkle path.

let mut current_hash = blake2s(cs, &leaf_bytes);

for (path_bit, path_witness) in path_selectors
    .into_iter()
    .zip(merkle_path_witness.into_iter())
{
    let left = UInt8::parallel_select(cs, path_bit, &path_witness, &current_hash);
    let right = UInt8::parallel_select(cs, path_bit, &current_hash, &path_witness);
    let mut input = [zero_u8; 64];
    input[0..32].copy_from_slice(&left);
    input[32..64].copy_from_slice(&right);

    current_hash = blake2s(cs, &input);
}

If it was a write request, then we update the root_hash. Otherwise, we enforce that it’s still the same.

current_root_hash = UInt8::parallel_select(
    cs,
    write_stage_in_progress,
    &current_hash,
    &current_root_hash,
);

for (a, b) in current_root_hash.iter().zip(current_hash.iter()) {
    Num::conditionally_enforce_equal(
        cs,
        should_compare_roots,
        &Num::from_variable(a.get_variable()),
        &Num::from_variable(b.get_variable()),
    );
}

In the end, we update state_diffs state.

for block in
    extended_state_diff_encoding.array_chunks::<{ keccak256::KECCAK_RATE_BYTES }>()
{
    keccak256_conditionally_absorb_and_run_permutation(
        cs,
        write_stage_in_progress,
        &mut diffs_keccak_accumulator_state,
        block,
    );
}

Final part

We need to run padding and one more permutation for final output.

keccak256_conditionally_absorb_and_run_permutation(
    cs,
    boolean_true,
    &mut diffs_keccak_accumulator_state,
    &padding_block,
);

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);
}

GitHub
GitHub
GitHub
GitHub
GitHub
GitHub