> For the complete documentation index, see [llms.txt](https://validium.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://validium.gitbook.io/docs/developer-reference/era-contracts/handling-l1-greater-than-l2-ops-on-zksync.md).

# Handling L1->L2 ops on ZKsync

***

The transactions on Validium can be initiated not only on L2, but also on L1. There are two types of transactions that can be initiated on L1:

* Priority operations. These are the kind of operations that any user can create.
* Upgrade transactions. These can be created only during upgrades.

### Prerequisites <a href="#prerequisites" id="prerequisites"></a>

Please read the full article on the [general system contracts / bootloader structure](/docs/developer-reference/era-contracts/system-contracts.md) as well as the pubdata structure with Boojum system to understand [the difference](/docs/developer-reference/era-contracts/handling-pubdata.md) between system and user logs.

### Priority operations <a href="#priority-operations" id="priority-operations"></a>

#### Initiation <a href="#initiation" id="initiation"></a>

A new priority operation can be appended by calling the [requestL2TransactionDirect](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/bridgehub/Bridgehub.sol#L216) or [requestL2TransactionTwoBridges](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/bridgehub/Bridgehub.sol#L262) methods on `BridgeHub` smart contract. `BridgeHub` will forward funds to the `SharedBridge` and send transaction request to the specified state transition contract (selected by the chainID). State transition contract will perform several checks for the transaction, making sure that it is processable and provides enough fee to compensate the operator for this transaction. Then, this transaction will be [appended](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol#L335) to the priority queue.

The difference between `requestL2TransactionDirect` and `requestL2TransactionTwoBridges` is that the `msg.sender` on the L2 Transaction is the second bridge in the `requestL2TransactionTwoBridges` case, while it is the `msg.sender` of the `requestL2TransactionDirect` in the first case. For more details read the [L1 ecosystem contracts](/docs/developer-reference/era-contracts/l1-ecosystem-contracts.md).

#### Bootloader <a href="#bootloader" id="bootloader"></a>

Whenever an operator sees a priority operation, it can include the transaction into the batch. While for normal L2 transaction the account abstraction protocol will ensure that the `msg.sender` has indeed agreed to start a transaction out of this name, for L1→L2 transactions there is no signature verification. In order to verify that the operator includes only transactions that were indeed requested on L1, the bootloader [maintains](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/system-contracts/bootloader/bootloader.yul#L1106-L1107) two variables:

* `numberOfPriorityTransactions` (maintained at `PRIORITY_TXS_L1_DATA_BEGIN_BYTE` of bootloader memory)
* `priorityOperationsRollingHash` (maintained at `PRIORITY_TXS_L1_DATA_BEGIN_BYTE + 32` of the bootloader memory)

Whenever a priority transaction is processed, the `numberOfPriorityTransactions` gets incremented by 1, while `priorityOperationsRollingHash` is assigned to `keccak256(priorityOperationsRollingHash, processedPriorityOpHash)`, where `processedPriorityOpHash` is the hash of the priority operations that has been just processed.

Also, for each priority transaction, we [emit](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/system-contracts/bootloader/bootloader.yul#L1100) a user L2→L1 log with its hash and result, which basically means that it will get Merklized and users will be able to prove on L1 that a certain priority transaction has succeeded or failed (which can be helpful to reclaim your funds from bridges if the L2 part of the deposit has failed).

Then, at the end of the batch, we [submit](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/system-contracts/bootloader/bootloader.yul#L4167-L4168) two L2→L1 log system log with these values.

#### Batch commit <a href="#batch-commit" id="batch-commit"></a>

During batch commit, the contract will remember those values, but not validate them in any way.

#### Batch execution <a href="#batch-execution" id="batch-execution"></a>

During batch execution, we would pop `numberOfPriorityTransactions` from the top of priority queue and [verify](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol#L328) that their rolling hash does indeed equal to `priorityOperationsRollingHash`.

### Upgrade transactions <a href="#upgrade-transactions" id="upgrade-transactions"></a>

#### Initiation <a href="#initiation-1" id="initiation-1"></a>

Upgrade transactions can only be created during a system upgrade. It is done if the `DiamondProxy` delegatecalls to the implementation that manually puts this transaction into the storage of the DiamondProxy, this could happen on calling [upgradeChainFromVersion](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol#L111) on the State Transition contract. Note, that since it happens during the upgrade, there is no “real” checks on the structure of this transaction. We do have [some validation](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol#L211), but it is purely on the side of the implementation which the `DiamondProxy` delegatecalls to and so may be lifted if the implementation is changed.

The hash of the currently required upgrade transaction is [stored](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol#L124) under `l2SystemContractsUpgradeTxHash`.

We will also track the batch where the upgrade has been committed in the `l2SystemContractsUpgradeBatchNumber` [variable](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol#L127).

We can not support multiple upgrades in parallel, i.e. the next upgrade should start only after the previous one has been complete.

#### Bootloader <a href="#bootloader-1" id="bootloader-1"></a>

The upgrade transactions are processed just like with priority transactions, with only the following differences:

* We can have only one upgrade transaction per batch & this transaction must be the first transaction in the batch.
* The system contracts upgrade transaction is not appended to `priorityOperationsRollingHash` and doesn’t increment `numberOfPriorityTransactions`. Instead, its hash is calculated via a system L2→L1 log *before* it gets executed. Note, that it is an important property. More on it [below](#security-considerations).

#### Commit <a href="#commit" id="commit"></a>

After an upgrade has been initiated, it will be required that the next commit batches operation already contains the system upgrade transaction. It is [checked](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol#L197) by verifying the corresponding L2→L1 log.

We also remember that the upgrade transaction has been processed in this batch (by amending the `l2SystemContractsUpgradeBatchNumber` variable).

#### Revert <a href="#revert" id="revert"></a>

In a very rare event when the team needs to revert the batch with the upgrade on Validium, the `l2SystemContractsUpgradeBatchNumber` is [reset](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol#L481).

Note, however, that we do not “remember” that certain batches had a version before the upgrade, i.e. if the reverted batches will have to be re-executed, the upgrade transaction must still be present there, even if some of the deleted batches were committed before the upgrade and thus didn’t contain the transaction.

#### Execute <a href="#execute" id="execute"></a>

Once batch with the upgrade transaction has been executed, we [delete](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol#L379) them from storage for efficiency to signify that the upgrade has been fully processed and that a new upgrade can be initiated.

### Security considerations <a href="#security-considerations" id="security-considerations"></a>

Since the operator can put any data into the bootloader memory and for L1→L2 transactions the bootloader has to blindly trust it and rely on L1 contracts to validate it, it may be a very powerful tool for a malicious operator. Note, that while the governance mechanism is generally trusted, we try to limit our trust for the operator as much as possible, since in the future anyone would be able to become an operator.

Some time ago, we *used to* have a system where the upgrades could be done via L1→L2 transactions, i.e. the implementation of the `DiamondProxy` upgrade would include a priority transaction (with `from` equal to for instance `FORCE_DEPLOYER`) with all the upgrade params.

In the Boojum though having such logic would be dangerous and would allow for the following attack:

* Let’s say that we have at least 1 priority operations in the priority queue. This can be any operation, initiated by anyone.
* The operator puts a malicious priority operation with an upgrade into the bootloader memory. This operation was never included in the priority operations queue / and it is not an upgrade transaction. However, as already mentioned above the bootloader has no idea what priority / upgrade transactions are correct and so this transaction will be processed.

The most important caveat of this malicious upgrade is that it may change implementation of the `Keccak256` precompile to return any values that the operator needs.

* When the`priorityOperationsRollingHash` will be updated, instead of the “correct” rolling hash of the priority transactions, the one which would appear with the correct topmost priority operation is returned. The operator can’t amend the behaviour of `numberOfPriorityTransactions`, but it won’t help much, since the the `priorityOperationsRollingHash` will match on L1 on the execution step.

That’s why the concept of the upgrade transaction is needed: this is the only transaction that can initiate transactions out of the kernel space and thus change bytecodes of system contracts. That’s why it must be the first one and that’s why [emit](https://github.com/matter-labs/era-contracts/blob/29f9ff4bbe12dc133c852f81acd70e2b4139d6b2/system-contracts/bootloader/bootloader.yul#L600) its hash via a system L2→L1 log before actually processing it.

#### Why it doesn’t break on the previous version of the system <a href="#why-it-doesnt-break-on-the-previous-version-of-the-system" id="why-it-doesnt-break-on-the-previous-version-of-the-system"></a>

This section is not required for current system understanding but for those willing to analyze the previous production system which wasn't susceptible to attack.

Note that the hash of the transaction is calculated before the transaction is executed: [era-system-contracts/bootloader/bootloader.yul#L1055](https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1055)

And then we publish its hash on L1 via a *system* L2→L1 log: [era-system-contracts/bootloader/bootloader.yul#L1133](https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1133)

In the new upgrade system, the `priorityOperationsRollingHash` is calculated on L2 and so if something in the middle changes the implementation of `Keccak256`, it may lead to the full `priorityOperationsRollingHash` be maliciously crafted. In the pre-Boojum system, we publish all the hashes of the priority transactions via system L2→L1 and then the rolling hash is calculated on L1. This means that if at least one of the hash is incorrect, then the entire rolling hash will not match also.
