PSE zkEVM
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    1
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    <style> table { white-space: nowrap; } </style> ## Introduciton State circuit iterates over random read-write access records of EVM circuit to verify each data is consistent between different writes. To verify if data is consistent, it first verify all access records are grouped by their unique identifier and sorted by order of access - a sequential counter named `rw_counter`, then verify the records between writes are consistent. It also verifies data is in correct format. It then serves as a lookup table for EVM circuit to do consistent random read-write access. ## Different models To have a complete random read-write access model, we can begin with the simplest one and extend it with features required by us step by step, for easier understanding. ### Read-only array Let's begin with the simplest model, say there is a VM accessible to a read-only array as random access memory [^cairo], which consumes a program as witness for execution. [^cairo]: Cairo also chooses to use read-only memory model, and **Ch8.5 - Read-write memory** explains things very clealy and I would recommend to have a look. Say the memory size could be up to <code>2^64^-1</code>, since the index of memory access is witness in program, for only VM itself it's impractical to track such large chunk of witnesses and use a giant multiplexer to select the value at the index. But without tracking witnesses, how do we know the random access memory is consistent in the execution trace? In PLONK (or [RAP](https://hackmd.io/@aztec-network/plonk-arithmetiization-air)) we could introduce challenge as randomness to achieve some "global" property, for example, poly <code>f~a~</code> is a shuffle of <code>f~b~</code>, which is required by us here. With such shuffle argument[^shuffle-argument], we could verify the random access memory is consistent. [^shuffle-argument]: Or "multiset check" described in [Ariel's writeup](/@arielg/ByFgSDA7D). I say "shuffle argument" because I'm afraid "permutation argument" might sound like the one used in PLONK, which I think is abbreviation of "permutation argument **with** identity map", so I would say "shuffle argument" to avoid ambiguity. In halo2 it uses both "permutation argument **with** identity map" and "permutation argument **without** identity map", [the former](https://zcash.github.io/halo2/design/proving-system/permutation.html) is also called copy constraint, and the latter is used as sub-argument in both copy constraint and lookup. For simplicify, we ask prover to put memory index and value in different polys <code>f~index~</code> and <code>f~value~</code>, then we let the prover to read the value at the index directly without any constraint. Then with <code>f~index~</code> and <code>f~value~</code> commited, we then ask prover to provide shuffle version of them <code>f’~index~</code> and <code>f’~value~</code>[^shuffle-on-random-linear-combination], and checks 2 things: [^shuffle-on-random-linear-combination]: To make sure <code>f’~index~</code> and <code>f’~value~</code> are shuffle in the same way, we can use random linear combination trick to ensure it. For example, we check if <code>f = f~index~ + αf~value~</code> is a shuffle of <code>f’ = f’~index~ + αf’~value~</code>, instead of checking them respectively, where the <code>α</code> is randomness derived from transcript. 1. Indices in <code>f’~index~</code> are sorted (either ascendingly or descendingly) [^continuous-read-only-memory] 2. Values in <code>f’~value~</code> remain the same as long as index remains the same [^continuous-read-only-memory]: In Cairo they are using even simpler model, they require the memory is continuous accessed (no any skip), then first check becomes "Indices in <code>f’~index~</code> is continuous". See **Ch9.7 - Nondeterministic continuous read-only memory** for the concrete constraints. And both checks here could be simply implemented with "local" constraints without tracking all of witnesses at a gate, thanks to the shuffle argument. ### Read-write array Most VMs are not designed to support a read-only array model, such as EVM. Since in our context we want to be as similar to EVM's original behavior as possible (not design a whole new VM to emulate EVM), so we need a read-write access to stack, memory, storage, etc... But instead of extending read-only array model to support so many things, let's begin with extending the read-only memory with write feature. So to support write, we add another poly <code>f~is_write~</code> to let the program specify if this access is a write. Then also ask prover to provide the shuffle of it as <code>f’~is_write~</code>, the checks now becomes: 1. Indices in <code>f’~index~</code> are sorted (either ascendingly or descendingly) 2. As long as index remains the same 2.1 Values in <code>f’~value~</code> remain the same when <code>f’~is_write~</code> is `false` 2.2 Values in <code>f’~value~</code> could be any value when <code>f’~is_write~</code> is `true` But there is a problem, it seems that in VM prover can read a value that is actually written in the future, but the shuffled polys still pass the constraints, because there is no constraint on how prover shuffles the access to the same index. This issue pops up because when we say "something is updatable", we are actually saying "something is updatable **over time**", the concept of time natually comes to play a important role. To resolve this issue, we introduce another poly <code>f~rw_counter~</code> [^rw_counter], and ask prover to increase this counter every time when it does random access. Then add another constraint before second point: [^rw_counter]: It's previously named `global_counter`, but I found there might be more counters in circuit, so to be more precise, I try to name it `rw_counter`. (I also considered about the name `timestamp`, but afraid that it woud be confused with the `timestamp` in block header) 1. ... 2. As long as index remains the same 2.0. Counters in <code>f’~rw_counter~</code> are sorted ascendingly. 2.1. ... ### Read-write dictionary Finally, because EVM can access to several kinds of data in the execution of a block, so we need a dictionary rather than an array for easier usage. Since we are operating on a large field, which have 254 bits to carry information, a naive approach is to encode as many information into <code>f~index~</code> as possible, and semantically turn <code>f~index~</code> into <code>f~key~</code>. For example, say we only have stack and memory, we can reserve the first bit as a domain separator to specify which kind of data we are accessing, and set the following 24 bits as call id, then set the following 40 bits as index of stack and memory. Another approach is to introduce more polys, and add more layers of sorting (e.g. add <code>f~tag~</code>, <code>f~call_id~</code> and sort it in order of <code>f~tag~</code>, <code>f~call_id~</code>, <code>f~index~</code>). Or we could even directly sort the random linear combination or these polys as the key (e.g. <code>f’~key~ = sorted(f~tag~ + βf~call_id~ + β^2^f~index~)</code>), but it might become annoying and more expensive to sort field elements instead of something with restricted range (e.g. 40 bits). ## Implementation ### Data kinds Here is the list of different kinds of data EVM can access during the execution. | Key 1 (Tag) | Key 2 | Key 3 | Key 4 | Value 1 | Aux 1 | Aux 2 | | ---------------------------- | ----------------------- | ----------------------- | ------------------- | --------- | ------------------------- | ---------------- | | `TxAccessListAccount` | `tx_id (24)` | `account_address (160)` | | `is_warm` | | | | `TxAccessListAccountStorage` | `tx_id (24)` | `account_address (160)` | `storage_key (rlc)` | `is_warm` | | | | `TxRefund` | `tx_id (24)` | | | `value` | | | | `Account` | `account_address (160)` | `field_tag (~)` | | `value` | | | | `AccountStorage` | `account_address (160)` | `storage_key (rlc)` | | `value` | `tx_id (24)` <sup>1</sup> | `committed_value` | | `CallContext` | `call_id (24)` | `field_tag (~)` | | `value` | | | | `Stack` | `call_id (24)` | `stack_pointer (10)` | | `value` | | | | `Memory` | `call_id (24)` | `memory_address (40)` | | `byte` | | | <sub style="left: 1em; bottom: 1em"> Note: <br> <strong>•</strong> <code>TxLog</code> and <code>AccountDestructed</code> are complicated so ignored for now. <br> <strong>•</strong> The number following is tha max amount of bits the key could contain. <br> <strong>•</strong> <code>~</code> means continous integer. <br> <strong>•</strong> <code>rlc</code> means random linear combination of EVM word, and it's a field element. <br> Also: <br> <strong>1.</strong> Since the gas cost function of <code>SSTORE</code> not only takes current and previous value, but also takes the original value finalized in previous transaction, so we need to track the last <code>value</code> as <code>committed_value</code> when <code>tx_id</code> changes. </sub> Currently the implementation try to have each key as a column, and verify they are sorted by comparing the difference to previous row is positive (in range of the bits). But we never have such large range lookup table, so using [running sum](https://github.com/zcash/orchard/blob/main/src/circuit/gadget/utilities/lookup_range_check.rs#L17) trick to verify it by chunks seems inevitable. As for random linear combination, we can decomposite it into 32 bytes and then compare. ### Property of different kinds Different kinds of data also have different properties we migt need to take extra care of: | Tag | Initial value | Reversible <sup>1</sup> | Write only persistent <sup>2</sup> | | ---------------------------- | ------------------ | ----------------------- | ---------------------------------- | | `TxAccessListAccount` | `0` <sup>3</sup> | Yes | | | `TxAccessListAccountStorage` | `0` | Yes | | | `TxRefund` | `0` | | Yes | | `Account` | `any` <sup>4</sup> | Yes | | | `AccountStorage` | `any` | Yes | | | `CallContext` | `any` <sup>5</sup> | | | | `Stack` | `any` <sup>6</sup> | | | | `Memory` | `0` <sup>7</sup> | | | <sub style="left: 1em; bottom: 1em"> Note: <br> <strong>1.</strong> When data is reversible, every write access to it might probably accompanied by a reversion write access in the future (with a larger <code>rw_counter</code> and value before the write). <br> <strong>2.</strong> Some reversible data will not affect the further execution of a tx like <code>TxRefund</code>, <code>TxLog</code> and <code>AccountDestructed</code> (<code>SELFDESTRUCT</code> takes effect in the end of tx). So they will only be written when the call is persistent. <br> <strong>3.</strong> <code>TxTxAccessList*</code> and <code>TxRefund</code> are always operated with a <code>diff</code>, so their initial values are constraint to be <code>0</code>. <br> <strong>4.</strong> <code>Account*</code>'s initial value will be verified with previous state/storage trie, but how to connect this with MPT circuit is yet to be determined. <br> <strong>5.</strong> <code>CallContext</code> is always operated properly, so no specific initial value, and we allow it to read before write <br> <strong>6.</strong> <code>Stack</code> is always operated by push and pop defined in circuit, so there will not be read without write situation. <br> <strong>7.</strong> <code>Memory</code> can be read without write because the memory is always expanded before execution implicitly, so their initial values are constraint to be <code>0</code>. </sub> ### Reversible data To achieve full equivalence with EVM, we need to support reversion. In EVM circuit, current approach to support reversion is to ask prover tell us if the state updates in this call are persistent or not, and if not what's the `rw_counter` end of reversion, for reverting the state update at the same step. So every time we encounter state update, we need to read the previous value first, and do the write in case we need reversion. To simplify this procedure, we can read the rotation of the <code>f~value~</code> to get access the value at previous row at the same time. ### Uniqueness of `rw_counter` The way we connect EVM circuit and State circuit is going to use shuffle argument [^halo2-does-not-have-api-for-shuffle-argument]. However, this doesn't make sure the `rw_counter` in State circuit are unique because in EVM circuit we might have some "unconstraint cells" (unused cells) that can be loaded with any value, so we need to make sure they are filled with `0` (the blue part in the illustration below) with some constraint. ![](https://hackmd.io/_uploads/SyXwHGWvc.png) [^halo2-does-not-have-api-for-shuffle-argument]: Interestingly, in `halo2`, they don't provide shuffle argument as an external api, even it already uses it in subset argument. So when we want to implement a shuffle argument, we will need a new api to query extra challenge from transcript after we have commit both circuits (see [implementation](https://github.com/appliedzkp/halo2/pull/62) and [other potential usages](https://github.com/zcash/halo2/issues/527) if we have such api). Another approach to make sure the blue part not showing in State circuit, we can introduce extra advice column `flag_rw`, which shows if this cell should exist in State circuit or not. To make this `flag_rw` have static cost, we can use "countdowner trick" which requires 2 extra advice column with few constraints. Previously the way to make sure `rw_counter` is unique in State circuit is to count how many rows in State circuit with `rw_counter != 0`, and accumulate this value then populate it as public input, then share with EVM circuit. This approach requires 2 extra columns with 1 extra public input. Not sure which approach has less complexity, still exploring. But in regard to aggregation, the less public input the better (even better if public inputs are not shared between circuits). ## Reference - [Multiset checks in PLONK and Plookup](/@arielg/ByFgSDA7D) - [The `halo2` Book](https://zcash.github.io/halo2/index.html) - [Cairo - a Turing-complete STARK-friendly CPU architecture](https://eprint.iacr.org/2021/1063.pdf)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully