peter-scroll
    • Create new note
    • Create a note from template
      • 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
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • 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
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync 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
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
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
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Sequencer The sequencer is one of the core components in the Scroll protocol: it is responsible for maintaining the L2 blockchain. The sequencer also ensures that the user and developer experience on Scroll closely resemble those on Ethereum. It achieves this by maintaining EVM and RPC behaviour that is directly inherited from Ethereum, with very few modifications. The primary functions of Scroll's sequencer are: - Collect transactions from L2 and L1. - Validate transactions and pack them into L2 blocks. - Execute blocks and maintain the L2 blockchain state. - Pack blocks into chunks for zkEVM proving. - Pack chunks into batches for aggregate proving. - Submit batches onto L1 (Ethereum), providing Data Availability. It is worth highlighting that bridging messages and tokens from L1 to L2 (deposits) is also the sequencer's responsibility. Messages from L2 to L1 (withdraws), on the other hand, can be executed by any user on L1. The secondary functions of Scroll's sequencer are: - Offer standard Ethereum RPC APIs and some Scroll extension APIs. - Allow peers (follower nodes) to sync the blockchain using Ethereum's peer-to-peer protocol. The sequencer, being a fork of [go-ethereum](https://github.com/ethereum/go-ethereum), inherits most of its functionality from Ethereum. This includes transaction and block data structures, EVM execution, RPC and p2p protocols. As these were not reimplemented but were directly inherited from [go-ethereum](https://github.com/ethereum/go-ethereum)'s original code, we can have a very high certainty of Scroll's compatibility with Ethereum. The following sections will introduce the main components of the sequencer, the end-to-end sequencing workflow, and some design details. *In the remainder of this article, L1 will refer to Ethereum, while L2 will refer to Scroll.* ## Sequencer Components The Scroll sequencer has two main components: `l2geth` and `rollup-relayer`. [`l2geth`](https://github.com/scroll-tech/go-ethereum) is Scroll's fork of [go-ethereum](https://github.com/ethereum/go-ethereum). It is responsible for building and executing blocks and maintaining the blockchain state. `l2geth` inherits most of its functionality from Ethereum, with some notable differences listed below. `l2geth` has the following submodules (not exhaustive list): - **Storage**: Ledger and state storage implemented using LevelDB. - **EVM**: The state transition rules of Ethereum. - **Worker:** Responsible for creating new L2 blocks. - **L1 `SyncService`**: Sync and store L1 messages in `l2geth` local database. - **API layer**: Standard Ethereum RPC and p2p interfaces. - **Transaction pool**: mempool for L2 transactions. `rollup-relayer` is responsible for splitting up the Scroll blockchain into sections that are suitable for proving and commitment on L1. The two main units of this are *chunks* and *batches*. A chunk is simply a contiguous section of the L2 ledger, it is a collection of L2 blocks, and chunks are the unit of zkEVM proving. `Batches`, on the other hand, are collections of chunks that are submitted to L1, along with an aggregate proof. `rollup-relayer` has the following submodules: - `chunk-proposer`: Collect L2 blocks, propose chunks while enforcing certain limits (see below). - `batch-proposer`: Collect chunks, propose batches while enforcing certain limits (see below). - `relayer`: Submit batches and batch proofs to L1. Furthermore, the following two Scroll contracts are closely related to the sequencer: - `L1MessageQueue` maintains a queue of messages that the sequencer must relay to L2. These messages are either submitted through the bridge (`L1ScrollMessenger`) as deposit transactions or through `EnforcedTxGateway` as enforced transactions. - `ScrollChain` maintains batches and their finalization status. Each batch is submitted through the `commitBatch` function, which stores its `batchHash` in this contract. The ZK proofs are submitted through the `finalizeBatchWithProof` function. ## Lifecycle of a transaction We will now present the end-to-end sequencing workflow from the perspective of a transaction. Transactions can originate either from L2 or from L1, but during sequencing these two types are processed mostly the same way. Most Scroll transactions originate on L2: 1. After signing the transaction, the user submits it to a Scroll node through the standard `eth_sendRawTransaction` API. 2. The transaction is broadcast through the Scroll peer-to-peer network until it eventually reaches the sequencer. Note that submitting transactions to the sequencer directly is not allowed. 3. The sequencer validates the transaction and stores it in its local transaction pool. Some transactions (deposit and enforced transactions) originate on L1: 1. The user submits the transaction on Ethereum. For deposit transactions, the user can use the `ScrollGatewayRouter` contract, one of the specific gateways like `StandardERC20Gateway`, or they can also submit the deposit to the `L1ScrollMessenger` contract directly. Enforced transactions are initiated through the `EnforcedTxGateway` contract. 2. The transaction is validated and appended to the `L1MessageQueue`, which then emits a `QueueTransaction` event. 3. Once the corresponding blocks on L1 are finalized, the sequencer `SyncService` then collects all new `QueueTransaction` events. For each `QueueTransaction` event, the sequencer constructs a special transaction (`L1MessageTx`) and stores it in its local database. The block creation and commit steps are shared for the two transaction types: 4. **Blocks:** The `l2geth` worker periodically starts a new mining job. It first selects a number of L1 messages from its local database, then a number of L2 transactions from its transaction pool for inclusion. The transactions are executed one by one and appended to the block. Once the worker has collected enough transactions, it seals the block, stores it in its local database and `l2geth` broadcasts it into the Scroll peer-to-peer network. 5. **Chunks, batches:** The `rollup-relayer` collects the new L2 block. Eventually, this block is included in a chunk. Eventually, that chunk is included in a batch. 6. **Commit:** The `rollup-relayer` commits the batch by calling `commitBatch` on the `ScrollChain` contract on L1. The data submitted with the commit transaction contains block headers and L2 transactions. (The submitted data does not contain L1 transactions, since these are already available in the `L1MessageQueue` contract on L1.) The batch hash is calculated by `ScrollChain` and stored in its contract storage. The finalization steps are as follows: 7. **Chunk proof:** The chunk is proven by a chunk prover (zkEVM prover). 8. **Batch proof:** For each chunk in the batch, the chunk proofs are aggregated into a single batch proof by the batch prover (aggregation prover). 9. **Finalization:** The batch proof is submitted to L1 by the `rollup-relayer`, along with the updated state root and withdraw root. `ScrollChain` verifies the submitted proof using an on-chain verifier contract. If verification passes, the batch is marked as finalized, and L1 messages processed in this batch are deleted from `L1MessageQueue`. At this point, the transactions included in the finalized batch are considered finalized. ## `l2geth`: Summary of modifications `l2geth` is a fork of `[go-ethereum](https://github.com/ethereum/go-ethereum)`. As such, it inherits most of Ethereum's behaviours. However, we needed to make some breaking changes to `l2geth` to enable more efficient proving. This section provides a non-exhaustive list of the modifications, along with their rationale. **zktrie** - Ethereum uses the MPT (Merkle-Partricia Trie) as its state storage data structure. This trie's structure and the fact that it uses keccak hash would make it prohibitively expensive for ZK circuits. l2geth instead uses zktrie: a binary trie with Poseidon hash for its state storage. **Opcodes**: - The `BASEFEE` opcode are disabled and behaves the same way as `INVALID`. The reason is that Scroll diabled EIP-1559. - The `SELFDESTRUCT` opcode is disabled and behaves the same way as `INVALID`. The reason is that this opcode would be prohibitively expensive to prove, and it is already deprecated in Ethereum. - `DIFFICULTY`/`PREVRANDAO` returns `0` since l2geth does not run PoW or PoS. - `BLOCKHASH` returns `keccak(chainId || height)`. The reason is that the full block hash cannot be verified by the zkEVM with reasonable cost, since one would need to provide all the receipts and the bloom filter to verify these fields. As a result, `BLOCKHASH` will be unique for each block but it will not correspond to the actual L2 blockhash and it cannot be used as a source of randomness. - The Shanghai EIPs: EIP-3860 (Limit and meter initcode), EIP-3651 (warm coinbase), PUSH0 are enabled on Scroll. That means that you can safely target the latest EVM version when compiling your contracts. **Precompiles** - The hashing precompile `sha256`, `ripemd-160`, and `blake2f` cannot be verified by the current zkEVM version and thus they are disabled. Calling these precompiles will revert. We plan to support these precompiles in the future in the form of a network upgrade. - `modexp` works but only supports inputs of 32 bytes (`uint256`) or shorter. `ecPairing` works but only allows up to 4 inputs. This limitations make proving these precompiles much more manageable. We believe that the majority of existing use cases fit into these limits. **Fees** - All fees collected on L2 are sent to a Scroll-maintained L2 fee vault contract. - L1 fee: In addition to the L2 gas fee that covers L2 block space and execution costs, we also collect an L1 fee that covers the costs of committing the transaction to L1. This fee is proportional to the size of the RLP-encoded transaction. The actual cost depends on the current settings stored in the `L1GasOracle` contract on L2. This fee is deducted from the sender balance directly (and not from the gas allowance). **L1MessageTx** - We added a new transaction type `L1MessageTx`. - We also added DB interfaces for storing such transactions and related metadata. - Finally, we implemented `SyncService` that monitors finalized blocks on L1 and collects L1 messages from these. **StateAccount** - Add `PoseidonCodeHash` to `StateAccount`. The zkEVM uses this code hash to verify the contract code when it's loaded. - Maintain `KeccakCodeHash` in `StateAccount`. The zkEVM only needs to verify this one (when the contract is created). The `EXTCODEHASH` opcode returns the `KeccakCodeHash`, maintaining compatibility with Ethereum. - Add `CodeSize` to `StateAccount`. The `EXTCODESIZE` opcode returns `CodeSize`. This way, we can verify that the correct result is returned without loading the contract code into the zkEVM. **New validation rules** - We added a number of new block validity rules to l2geth that correspond to the "chunk and batch constraints" outlined above: Number of l2 transactions, block payload size, circuit row consumption. - Other rules are related to L1 messages. L1 messages in a block must form a contiguous block at the front of the block. L1 messages in a block must be included following their order in `L1MessageQueue`, i.e. they must be included with increasing `QueueIndex`. L1 messages in a block must match those in `L1MessageQueue`. - Most of these rules are considered both during block creation (worker.go) and block validation (block_validation.go). As a result, if the sequencer violates any of these rules, follower nodes will reject its blocks. **Other changes** - Disable EIP1559. - Offer a trace api `scroll_traceBlockByNumberOrHash` to provers. ## `l2geth`: Skipping & circuit capacity checker With Scroll's current zkEVM circuits, it is possible to construct transactions and blocks that cannot be proven because they "do not fit into the zkEVM". We call this *proof overflow*. To avoid this, we implemented a *circuit capacity checker* module as part of l2geth. The circuit capacity checker is used both during block creation (`worker.go`) and block validation (`block_validation.go`). During block creation, if the next transaction would lead to proof overflow, then we seal the block and leave the transaction for the next block. If a single transaction leads to proof overflow, l2geth discards it. We call this mechanism *skipping*. Skipping L2 transactions means simply discarding them. In this case, the user needs to submit another transaction with the same nonce that does not lead to proof overflow in order to proceed. Skipping L1 messages is a more explicit process. Both the `ScrollChain` contract on L1 and the zkEVM verifies that each L1 transaction in `L1MessageQueue` was either included and executed, or skipped. While the batch encoding does not contain L1 transactions, it does include a *skip bitmap* that indicates to `ScrollChain` which L1 messages were skipped. If an L1 deposit message is skipped, the user can get their tokens refunded on L1. Note: Proof overflow and thus skipping suggests malicious activity; for normal deposit transactions, this will never happen. As the unit of zkEVM proving is the chunk, proof overflow must be avoided for chunks as well. This is achieved by incorporating the circuit capacity checker into the rollup-relayer to make sure that it never proposes unprovable chunks. ## `rollup-relayer`: Chunk and batch constraints Compared to `l2geth`, the `rollup-relayer` is a much more straightforward and simple component. It collects L2 blocks, creates chunks and batches, and submits these to L1. In this section we outline the main limits that `rollup-relayer` must enforce during chunk and batch creation. Constraints on chunks: - **No proof overflow:** Using the circuit capacity checker, we make sure to include as many blocks in a chunk that it would still not lead to proof overflow. Constraints on batches: - **L1 payload:** Ethereum has a hardcoded 128KB limit on transaction payload size. - **L1 commit gas:** The gas cost of committing a batch is proportional to the number of blocks and transactions in it. We must make sure that this cost does not exceed the L1 block gas limit. - **Number of chunks:** `rollup-relayer` includes a number of chunks that is optimal for the aggregation prover. ## Notes on decentralizing the sequencer As it stands, Scroll's current sequencer is centralized. In this section, we consider 4 threats related to centralized sequencers: arbitrary state transitions, censorship risk, regulatory risk, liveness risk. A centralized sequencer cannot execute arbitrary state transitions. If, for example, the sequencer tried to move funds from a user's account to its own account, that would constitute an invalid state transition according to the EVM's rules. While the sequencer could certainly create such a block, it could not be proven in the zkEVM and thus the funds could never be withdrawn to L1. A centralized sequencer could choose to censor transactions from certain senders, rendering their funds on L2 frozen. We added the enforced transaction mechanism to mitigate this risk. Through this mechanism, users can submit transaction on L1 directly and the sequencer is forced to include such messages in its blocks. Unfortunately, the problem of proof overflow forces us to allow the sequencer to skip such messages. This kind of censorship is very explicit and easy to detect. Scroll is working actively to eliminate the proof overflow problem through upgrades to our proving system. Once proof overflow does not necessitate skipping anymore, the enforced transaction mechanism can fully solve censorship. Since the centralized sequencer must be deployed in some specific jurisdiction, it is subject to regulatory risk. Regulators can force Scroll to enforce KYC requirements, or potentially event take over the sequencer. While this is certainly a possibility, it is possible in such scenarios to quickly migrate the sequencer to other jurisdictions. The long-term solution to this risk is decentralizing the sequencer. If the centralized sequencer is down for any reason, the L2 ledger cannot progress and users cannot withdraw their funds. The long-term solution to this risk is decentralizing the sequencer. Decentralizing the sequencer helps prevent both malicious acts from the sequencer, as well as Single-Point-of-Failure issues. Scroll is actively working on upgrading the protocol to allow multiple sequencers. We will share the progress with the community shortly.

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