Processing of Transactions

Learn how transactions are processed

This section describes how transactions are processed. There are 2 flows of transaction processing, one for L1 transactions and one for L2 transactions. We'll describe the latter first and the L1 -> L2 transactions section will explain the differences in the former.

  1. Parsing: The transaction is parsed to extract its details.
  2. Resource Calculation: The bootloader computes the transaction's resource usage, including gas limits and intrinsic costs.
  3. Nonce Validation: The bootloader ensures the transaction's nonce is unused.
  4. Fee Charging: The required transaction fees are calculated and deducted.
  5. Execution: The transaction is executed, either as a call or a deployment.
  6. Refunding: Unused resources are refunded.

Resources

Transaction processing starts with the parsing of the transaction.

After parsing the transaction, the bootloader computes how many resources the transaction can spend. This resource calculation involves taking the transaction's gas limit, checking it's bounded by the block's gas limit and subtracting calldata and intrinsic gas costs.

Validation

The first step of validation done by the bootloader is checking that the nonce is not used. This is done by calling the corresponding account model method. We use incremental nonces like the ones in Ethereum. Note that for this operation, and many like it, the bootloader needs to access the storage through the System.

Then the account model validation method is called. This performs the balance check for the token transfer, signature verification, and nonce increase.

The next step is charging the fees. This involves computing the required funds (gas_price * gas_limit), and performing the transfer to the bootloader's formal address.

Pubdata Considerations

ZKsync OS has to publish some data to the settlement layer to ensure the rollup's state can be recovered at any time. This what we call "pubdata", and includes state diffs after a block application and some required preimages. Given that making this data available costs money, we need to ensure that a transaction has enough resources to pay for its share of pubdata. Thus, the final step of validation is charging for the pubdata size produced after validation.

Execution

Validation can fail for a number of different reasons, detailed in the InvalidTransaction type in the Errors module. It it succeeds, the bootloader proceeds to execute the transaction.

This step is straightforward: the bootloader simply calls the account mode's execution method.

This consists of determining whether it's a normal call or a deployment. In the first case, the call is processed by the run_single_interaction helper. In the case of deployment, the bootloader will charge some extra intrinsic gas and perform the check on the init code size. After this, the deployment is processed with a call to run_till_completion.

After transaction execution, the bootloader checks if there are enough resources left for pubdata.

Refunding the caller

The last step in the transaction processing is the refund of unused resources.

Lifecycle of a transaction in the bootloader

To summarize, the lifecycle of an L2 transaction can be visualized as follows:

zksyncOS.png

L1 -> L2 transactions

The main difference in the processing of L1 transactions is that there is no validation step. The value for this transaction is minted, as those funds are now locked in the L1 bridge. For now, deployment transactions from L1 transactions are not supported.


Made with ❤️ by the ZKsync Community