Table of Contents
Summary
- Type
- Layer 2
- Timeline
- From 2024-01-15
- To 2024-01-19
- Languages
- Solidity, Yul
- Total Issues
- 12 (10 resolved, 2 partially resolved)
- Critical Severity Issues
- 0 (0 resolved)
- High Severity Issues
- 0 (0 resolved)
- Medium Severity Issues
- 1 (1 resolved)
- Low Severity Issues
- 2 (1 resolved, 1 partially resolved)
- Notes & Additional Information
- 9 (8 resolved, 1 partially resolved)
Scope
We audited specific files in the zk-4844 branch of the matter-labs/era-contracts repository, at commit abcbaf3. New contracts were audited in full, whereas the rest of the in-scope codebase was audited only for the diffs.
In scope were the following files:
l1-contracts
├── contracts
│ ├── common
│ │ └── L2ContractAddresses.sol
│ └── zksync
│ ├── Config.sol
│ ├── facets
│ │ └── Executor.sol
│ ├── interfaces
│ │ └── IExecutor.sol
│ └── utils
│ └── BlobVersioniedHash.yul
system-contracts
├── Constants.sol
├── L1Messenger.sol
├── PubdataChunkPublisher.sol
├── interfaces
│ └── IPubdataChunkPublisher.sol
└── bootloader
└── bootloader.yul
System Overview
This system upgrade has been executed to support proto-danksharding and the utilization of blob-carrying transactions defined in EIP-4844. With proto-danksharding, the protocol can now make use of cheaper storage for pubdata on L1. This allows for packing more transactions per batch, resulting in cheaper transactions. At a contract level, the protocol now supports processing pubdata via blobs, in addition to supporting the processing of pubdata via calldata. This will also set the stage for future changes aimed at separating pubdata publishing from batch commitment, thereby enabling the handling of different data availability solutions.
The changes in this upgrade span across both L2 system contracts and L1 zkSync contracts, namely Executor.sol
. Two new contracts were also included to support the additional changes. The PubdataChunkPublisher
contract was added on the L2 system contracts side, whereas the BlobVersionedHash
contract was added to the L1 zkSync contracts. The added contracts will be explained in detail below, followed by an outline of changes for the existing contracts.
PubdataChunkPublisher
Contract
The new PubdataChunkPublisher
system contract takes the full pubdata, creates chunks, and commits them in the form of two system logs. The primary purpose of this is to commit to blobs and have those commitments travel to L1 via system logs.
BlobVersionedHash
Contract
The EIP-4844 introduces a new opcode called BLOBHASH
. Since Solidity does not yet natively support calling the BLOBHASH
opcode, the temporary BlobVersionedHash.yul
contract had to be added, which acts in place of the eventual BLOBHASH
opcode. Once support for BLOBHASH
is added to Solidity, all calls to the BlobVersionedHash
contract will be substituted for calls to the BLOBHASH
opcode.
Bootloader
Contract
With the increase in the amount of pubdata due to blobs, changes have been made to the bootloader memory to facilitate more L2-to-L1 logs, compressed bytecodes, and pubdata.
Executor
Contract
While the function signature for commitBatches
and the structure of CommitBatchInfo
stays the same, the format of CommitBatchInfo::pubdataCommitments
changes. Prior to EIP-4844, this field held a byte array of pubdata. Now, it can either hold the total pubdata as before or a list of concatenated information for KZG blob commitments.
In order to distinguish between the two, a header byte is added to the byte array. This header byte indicates the source of pubdata, with 0 representing calldata and 1 representing blobs. Any other values in the first byte are rejected. The list of supported values for the header byte can be expanded in the future.
Trust Assumptions
This upgrade does not introduce new roles to the system. The implementation depends on the temporary BlobVersionedHash.yul
contract serving as a placeholder for the upcoming BLOBHASH
opcode. It is assumed that once support for the BLOBHASH
opcode is implemented, calls to the BlobVersionedHash
contract will be replaced with the utilization of the BLOBHASH
opcode.
Medium Severity
Bootloader Uses Incorrect Value for System Contract Upgrade Log Key
The SystemLogKey
enum is employed by the L2 system contracts to distinguish between logs. As per its definition, the value assigned to EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY
is 9
. However, in the bootloader.yul
file, the protocolUpgradeTxHashKey
function associated with EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY
returns the value 7
. This inconsistency may lead to unexpected behavior during the processing of L2-to-L1 logs.
Consider changing the return value of the protocolUpgradeTxHashKey
function to the correct value matching EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY
, which is 9
.
Update: Resolved in pull request #178 at commit 88d22b5.
Low Severity
require
Statement With Multiple Conditions
In Executor.sol
, there is a require
statement that checks multiple conditions to be satisfied.
To simplify the codebase and show the most helpful error messages for failing require
statements, consider having a single require
statement per condition.
Update: Resolved in pull request #179 at commit cf63da3.
Missing Tests
The proposed changes to the system contracts lack tests that could confirm the correctness of the implementation. For instance, the chunkAndPublishPubdata
function of the PubdataChunkPublisher
contract requires additional testing.
Consider adding tests to enhance the quality and health of the codebase.
Update: Partially resolved in pull request #188 at commit 43e3ecd. The Matter Labs team stated:
We added tests for failure scenarios with relevant revert checks. For success cases, we couldn't check whether the correct system log was published within the test, but have verified it manually.
Notes & Additional Information
Misleading Documentation
Documentation is misleading at some places in the codebase. For instance, the NatSpec comment describing MAX_MEM_SIZE
suggests that the memory page consists of 24000000 / 32
VM words. However, the value returned is 30000000
.
Consider rephrasing misleading comments to match the intention of the code.
Update: Resolved in pull request #180, at commit 84fbcfd.
Todo Comments in the Code
During development, having well-described TODO/Fixme comments will make the process of tracking and solving them easier. Without this information, these comments might age and important information for the security of the system might be forgotten by the time the system is released to production. These comments should be tracked in the project's issue backlog and resolved before the system deploys.
Multiplies instances of TODO/Fixme comments were found in the codebase:
- The
TODO
comment at line 44 ofExecutor.sol
- The
TODO
comment at line 23 ofPubdataChunkPublisher.sol
Consider removing all instances of TODO/Fixme comments and instead tracking them in the issues backlog. Alternatively, consider linking each inline TODO/Fixme comment to the corresponding issues backlog entry.
Update: Resolved in pull request #181, at commit d23d5c3.
Lack of Security Contact
Providing a specific security contact (such as an email or ENS name) within a smart contract significantly simplifies the process for individuals to communicate if they identify a vulnerability in the code. This practice is quite beneficial as it permits the code owners to dictate the communication channel for vulnerability disclosure, eliminating the risk of miscommunication or failure to report due to a lack of knowledge on how to do so. In addition, if the contract incorporates third-party libraries and a bug surfaces in those, it becomes easier for the maintainers of those libraries to contact the appropriate person about the problem and provide mitigation instructions. The IPubdataChunkPublisher
interface does not appear to have a security contact.
Consider adding a NatSpec comment containing a security contact above the contract definitions. Using the @custom:security-contact
convention is recommended as it has been adopted by the OpenZeppelin Wizard and the ethereum-lists.
Update: Resolved in pull request #182 at commit 54e982e.
Uninitialized Local Variable
The local variable blobVersionedHash
in Executor.sol
is declared and subsequently initialized with the return value of _getBlobVersionedHash
function later in the code.
To enhance the overall clarity, intent, and readability of the codebase, consider declaring and initializing the blobVersionedHash
variable in a single line.
Update: Resolved in pull request #183 at commit 0edd4ed.
Addresses Are Not Ordered Correctly
The constant values of the addresses in the L2ContractAddresses
contract are not ordered incrementally which is error-prone.
Consider ordering all addresses incrementally.
Update: Resolved in pull request #184 at commit 4da7ca8.
Typographical Errors
The following typographical errors were identified throughout the codebase:
- Instead of using "
4844
" [1] [2] [3] [4] [6] [7] [8], consider using "EIP-4844
". - Instead of "
EIP 4844
", consider using "EIP-4844
". - "failer" should be "failure"
- "doesnt" should be "doesn't"
- "isnt" should be "isn't"
- "arent" should be "aren't"
- "blobHah" should be "blobHash"
Consider fixing the above errors for improved clarity and readability.
Update: Resolved in pull request #185 at commit e9479f2.
Unused Code
In the Executor
contract, the check on line 189 implies that the number of _newBatchesData
is equal to 1. This check makes the following parts of the code irrelevant in subsequent executions:
- The check on line 192 is obsolete because
_newBatchesData.length
is equal to 1 (i.e., greater than 0). - Both
for
-loops on lines 216 and 248 can be refactored. If the length of the_newBatchesData
array is known to be 1, there is no value in iterating over it.
Moreover, consider restructuring the commitBatches
function to only accept data from a single new batch instead of a CommitBatchInfo
array. Furthermore, consider assessing further interdependent functions within the Executor
contract to accommodate this modification.
Update: Partially resolved in pull request #186 at commit 2aeb40a. The Matter Labs team stated:
We chose to remove the require on length being greater than 0 as the only change. Supporting 1 batch per commitment is a temporary change so leaving the code minimizes unnecessary work.
Chunks Are Published Before Data Is Validated
The publishPubdataAndClearState
function of L1Messenger
contract calls the pubdata chunk publisher contract in order to chunk and publish data. The issue lies in the fact that the call occurs before the check that ensures that the calldata does not contain extra data.
Consider swapping the checks to ensure that the data is in the correct format before calling the pubdata chunk publisher contract.
Update: Resolved in pull request #187 at commit c53456f.
Missing Docstrings
Throughout the codebase, there are several parts that have an incomplete docstring:
- The
IPubdataChunkPublisher
interface is missing docstrings. - The
BlockCommit
event inIExecutor.sol
is missing docstrings for thebatchNumber
,batchHash
, andcommitment
parameters. - The
BlocksVerification
event inIExecutor.sol
is missing docstrings for thepreviousLastVerifiedBatch
andcurrentLastVerifiedBatch
parameters. - The
BlockExecution
event inIExecutor.sol
is missing docstrings forbatchNumber
,batchHash
, andcommitment
parameters. - The
BlocksRevert
event inIExecutor.sol
is missing documentation for thetotalBatchesCommitted
,totalBatchesVerified
, andtotalBatchesExecuted
parameters. - The
sendL2ToL1Log
function inL1Messenger.sol
is missing docstrings for the_isService
,_key
and_value
parameters. - The
sendToL1
function inL1Messenger.sol
is missing docstrings for the_message
parameter. - The
requestBytecodeL1Publication
function inL1Messenger.sol
is missing docstrings for the_bytecodeHash
parameter.
Consider thoroughly documenting all functions (and their parameters) that are part of any contract's public API. Functions implementing sensitive functionality, even if not public
, should be clearly documented as well. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).
Update: Resolved in pull request #189 at commit f244ede.
Conclusion
The system upgrade introduces proto-danksharding and implements blob-carrying transactions as per EIP-4844, resulting in cheaper transactions on L1. The upgrade affects L2 system contracts, such as the addition of PubdataChunkPublisher
for committing pubdata to blobs and BlobVersionedHash
for handling the new BLOBHASH
opcode. The Bootloader
contract is adjusted to accommodate increased pubdata, while the Executor
contract undergoes changes in the format of pubdata commitments to support both calldata and blob commitments, distinguished by a header byte. The added contracts and modifications set the foundation for future data availability solutions.
The audit uncovered one issue of medium severity in addition to several other issues of low severity. Various recommendations have also been made to enhance the quality and documentation of the codebase. We found the dedicated documentation provided by Matter Labs team to be very helpful in understanding the audited code changes. Throughout the audit period, the Matter Labs team was very supportive and answered all questions we had in a timely manner.