Transaction Hash:
Block:
17069861 at Apr-17-2023 11:51:59 PM +UTC
Transaction Fee:
0.050444133001126368 ETH
$109.21
Gas Used:
1,289,104 Gas / 39.131158542 Gwei
Emitted Events:
| 219 |
DiamondProxy.0x8f2916b2f2d78cc5890ead36c06c0f6d5d112c7e103589947e8e2f0d6eddb763( 0x8f2916b2f2d78cc5890ead36c06c0f6d5d112c7e103589947e8e2f0d6eddb763, 0x000000000000000000000000000000000000000000000000000000000000447e, 0xaeae1a66285cad7cb1e2aa2c58bd256e149bc91b5519cbc2086cdff968cdb672, 0x9a0c4ebddf4135ad0450cee0314181a32a307e0245c0b7d058e0827740318585 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x112200Ea...d56B82211 |
39.226430093117599009 Eth
Nonce: 35975
|
39.175985960116472641 Eth
Nonce: 35976
| 0.050444133001126368 | ||
| 0x32400084...60a000324 | (zkSync Era: Diamond Proxy) | ||||
| 0x3dB52cE0...F2360d919 | (zkSync Era: Validator Timelock) | ||||
|
0x690B9A9E...Db4FaC990
Miner
| (builder0x69) | 2.202456261851233861 Eth | 2.202615217567034485 Eth | 0.000158955715800624 |
Execution Trace
ValidatorTimelock.commitBlocks( [{name:blockNumber, type:uint64, order:1, indexed:false, value:17533, valueString:17533}, {name:blockHash, type:bytes32, order:2, indexed:false, value:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA, valueString:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:10668656, valueString:10668656}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:34, valueString:34}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97, valueString:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB, valueString:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB}, {name:timestamp, type:uint256, order:7, indexed:false, value:1681774157, valueString:1681774157}, {name:commitment, type:bytes32, order:8, indexed:false, value:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7, valueString:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7}], _newBlocksData= )
DiamondProxy.0c4dd810( )-
ExecutorFacet.commitBlocks( _lastCommittedBlockData=[{name:blockNumber, type:uint64, order:1, indexed:false, value:17533, valueString:17533}, {name:blockHash, type:bytes32, order:2, indexed:false, value:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA, valueString:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:10668656, valueString:10668656}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:34, valueString:34}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97, valueString:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB, valueString:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB}, {name:timestamp, type:uint256, order:7, indexed:false, value:1681774157, valueString:1681774157}, {name:commitment, type:bytes32, order:8, indexed:false, value:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7, valueString:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7}], _newBlocksData= )
-
File 1 of 3: ValidatorTimelock
File 2 of 3: DiamondProxy
File 3 of 3: ExecutorFacet
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() external {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
interface IExecutor {
/// @notice Rollup block stored data
/// @param blockNumber Rollup block number
/// @param blockHash Hash of L2 block
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param priorityOperationsHash Hash of all priority operations from this block
/// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
/// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
/// @param commitment Verified input for the zkSync circuit
struct StoredBlockInfo {
uint64 blockNumber;
bytes32 blockHash;
uint64 indexRepeatedStorageChanges;
uint256 numberOfLayer1Txs;
bytes32 priorityOperationsHash;
bytes32 l2LogsTreeRoot;
uint256 timestamp;
bytes32 commitment;
}
/// @notice Data needed to commit new block
/// @param blockNumber Number of the committed block
/// @param timestamp Unix timestamp denoting the start of the block execution
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param newStateRoot The state root of the full state tree
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
/// @param priorityOperationsHash Hash of all priority operations from this block
/// @param initialStorageChanges Storage write access as a concatenation key-value
/// @param repeatedStorageChanges Storage write access as a concatenation index-value
/// @param l2Logs concatenation of all L2 -> L1 logs in the block
/// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
/// @param factoryDeps array of l2 bytecodes that were marked as known on L2
struct CommitBlockInfo {
uint64 blockNumber;
uint64 timestamp;
uint64 indexRepeatedStorageChanges;
bytes32 newStateRoot;
uint256 numberOfLayer1Txs;
bytes32 l2LogsTreeRoot;
bytes32 priorityOperationsHash;
bytes initialStorageChanges;
bytes repeatedStorageChanges;
bytes l2Logs;
bytes[] l2ArbitraryLengthMessages;
bytes[] factoryDeps;
}
/// @notice Recursive proof input data (individual commitments are constructed onchain)
struct ProofInput {
uint256[] recursiveAggregationInput;
uint256[] serializedProof;
}
function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
external;
function proveBlocks(
StoredBlockInfo calldata _prevBlock,
StoredBlockInfo[] calldata _committedBlocks,
ProofInput calldata _proof
) external;
function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
function revertBlocks(uint256 _newLastBlock) external;
/// @notice Event emitted when a block is committed
event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
/// @notice Event emitted when blocks are verified
event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
/// @notice Event emitted when a block is executed
event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
/// @notice Event emitted when blocks are reverted
event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "./interfaces/IExecutor.sol";
/// @author Matter Labs
/// @notice Intermediate smart contract between the validator EOA account and the zkSync smart contract.
/// @dev The primary purpose of this contract is to provide a trustless means of delaying block execution without
/// modifying the main zkSync contract. As such, even if this contract is compromised, it will not impact the main contract.
/// @dev zkSync actively monitors the chain activity and reacts to any suspicious activity by freezing the chain.
/// This allows time for investigation and mitigation before resuming normal operations.
/// @dev The contract overloads all of the 4 methods, that are used in state transition. When the block is committed, the
/// timestamp is stored for it. Later, when the owner calls the block execution, the contract checks that block
/// was committed not earlier than X time ago.
contract ValidatorTimelock is IExecutor, Ownable2Step {
/// @notice The delay between committing and executing blocks is changed.
event NewExecutionDelay(uint256 _newExecutionDelay);
/// @notice The validator address is changed.
event NewValidator(address _oldValidator, address _newValidator);
/// @dev The main zkSync smart contract.
address public immutable zkSyncContract;
/// @dev The mapping of L2 block number => timestamp when it was commited.
mapping(uint256 => uint256) public committedBlockTimestamp;
/// @dev The address that can commit/revert/validate/execute blocks.
address public validator;
/// @dev The delay between committing and executing blocks.
uint256 public executionDelay;
constructor(
address _initialOwner,
address _zkSyncContract,
uint256 _executionDelay,
address _validator
) {
_transferOwnership(_initialOwner);
zkSyncContract = _zkSyncContract;
executionDelay = _executionDelay;
validator = _validator;
}
/// @dev Set new validator address.
function setValidator(address _newValidator) external onlyOwner {
address oldValidator = validator;
validator = _newValidator;
emit NewValidator(oldValidator, _newValidator);
}
/// @dev Set the delay between committing and executing blocks.
function setExecutionDelay(uint256 _executionDelay) external onlyOwner {
executionDelay = _executionDelay;
emit NewExecutionDelay(_executionDelay);
}
/// @notice Checks if the caller is a validator.
modifier onlyValidator() {
require(msg.sender == validator, "8h");
_;
}
/// @dev Records the timestamp for all provided committed blocks and make
/// a call to the zkSync contract with the same calldata.
function commitBlocks(StoredBlockInfo calldata, CommitBlockInfo[] calldata _newBlocksData) external onlyValidator {
for (uint256 i = 0; i < _newBlocksData.length; ++i) {
committedBlockTimestamp[_newBlocksData[i].blockNumber] = block.timestamp;
}
_propagateToZkSync();
}
/// @dev Make a call to the zkSync contract with the same calldata.
/// Note: If the block is reverted, it needs to be committed first before the execution.
/// So it's safe to not override the committed blocks.
function revertBlocks(uint256) external onlyValidator {
_propagateToZkSync();
}
/// @dev Make a call to the zkSync contract with the same calldata.
/// Note: We don't track the time when blocks are proven, since all information about
/// the block is known on the commit stage and the proved is not finalized (may be reverted).
function proveBlocks(
StoredBlockInfo calldata,
StoredBlockInfo[] calldata,
ProofInput calldata
) external onlyValidator {
_propagateToZkSync();
}
/// @dev Check that blocks were committed at least X time ago and
/// make a call to the zkSync contract with the same calldata.
function executeBlocks(StoredBlockInfo[] calldata _newBlocksData) external onlyValidator {
for (uint256 i = 0; i < _newBlocksData.length; ++i) {
uint256 commitBlockTimestamp = committedBlockTimestamp[_newBlocksData[i].blockNumber];
// Note: if the `commitBlockTimestamp` is zero, that means either:
// * The block was committed, but not though this contract.
// * The block wasn't committed at all, so execution will fail in the zkSync contract.
// We allow executing such blocks.
require(block.timestamp > commitBlockTimestamp + executionDelay, "5c"); // The delay is not passed
}
_propagateToZkSync();
}
/// @dev Call the zkSync contract with the same calldata as this contract was called.
/// Note: it is called the zkSync contract, not delegatecalled!
function _propagateToZkSync() internal {
address contractAddress = zkSyncContract;
assembly {
// Copy function signature and arguments from calldata at zero position into memory at pointer position
calldatacopy(0, 0, calldatasize())
// Call method of the zkSync contract returns 0 on error
let result := call(gas(), contractAddress, 0, 0, calldatasize(), 0, 0)
// Get the size of the last return data
let size := returndatasize()
// Copy the size length of bytes from return data at zero position to pointer position
returndatacopy(0, 0, size)
// Depending on the result value
switch result
case 0 {
// End execution and revert state changes
revert(0, size)
}
default {
// Return data with length of size at pointers position
return(0, size)
}
}
}
}
File 2 of 3: DiamondProxy
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
library UncheckedMath {
function uncheckedInc(uint256 _number) internal pure returns (uint256) {
unchecked {
return _number + 1;
}
}
function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
unchecked {
return _lhs + _rhs;
}
}
}
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
import "./libraries/Diamond.sol";
/// @title Diamond Proxy Contract (EIP-2535)
/// @author Matter Labs
contract DiamondProxy {
constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) {
// Check that the contract is deployed on the expected chain.
// Thus, the contract deployed by the same Create2 factory on the different chain will have different addresses!
require(_chainId == block.chainid, "pr");
Diamond.diamondCut(_diamondCut);
}
/// @dev 1. Find the facet for the function that is called.
/// @dev 2. Delegate the execution to the found facet via `delegatecall`.
fallback() external payable {
Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
// Check whether the data contains a "full" selector or it is empty.
// Required because Diamond proxy finds a facet by function signature,
// which is not defined for data length in range [1, 3].
require(msg.data.length >= 4 || msg.data.length == 0, "Ut");
// Get facet from function selector
Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig];
address facetAddress = facet.facetAddress;
require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
assembly {
// The pointer to the free memory slot
let ptr := mload(0x40)
// Copy function signature and arguments from calldata at zero position into memory at pointer position
calldatacopy(ptr, 0, calldatasize())
// Delegatecall method of the implementation contract returns 0 on error
let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
// Get the size of the last return data
let size := returndatasize()
// Copy the size length of bytes from return data at zero position to pointer position
returndatacopy(ptr, 0, size)
// Depending on the result value
switch result
case 0 {
// End execution and revert state changes
revert(ptr, size)
}
default {
// Return data with length of size at pointers position
return(ptr, size)
}
}
}
}
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../../common/libraries/UncheckedMath.sol";
/// @author Matter Labs
/// @notice The helper library for managing the EIP-2535 diamond proxy.
library Diamond {
using UncheckedMath for uint256;
using SafeCast for uint256;
/// @dev Magic value that should be returned by diamond cut initialize contracts.
/// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts.
bytes32 constant DIAMOND_INIT_SUCCESS_RETURN_VALUE =
0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1
/// @dev Storage position of `DiamondStorage` structure.
bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1;
event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);
/// @dev Utility struct that contains associated facet & meta information of selector
/// @param facetAddress address of the facet which is connected with selector
/// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored
/// @param isFreezable denotes whether the selector can be frozen.
struct SelectorToFacet {
address facetAddress;
uint16 selectorPosition;
bool isFreezable;
}
/// @dev Utility struct that contains associated selectors & meta information of facet
/// @param selectors list of all selectors that belong to the facet
/// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored
struct FacetToSelectors {
bytes4[] selectors;
uint16 facetPosition;
}
/// @notice The structure that holds all diamond proxy associated parameters
/// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION`
/// @param selectorToFacet A mapping from the selector to the facet address and its meta information
/// @param facetToSelectors A mapping from facet address to its selector with meta information
/// @param facets The array of all unique facet addresses that belong to the diamond proxy
/// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible
struct DiamondStorage {
mapping(bytes4 => SelectorToFacet) selectorToFacet;
mapping(address => FacetToSelectors) facetToSelectors;
address[] facets;
bool isFrozen;
}
/// @dev Parameters for diamond changes that touch one of the facets
/// @param facet The address of facet that's affected by the cut
/// @param action The action that is made on the facet
/// @param isFreezable Denotes whether the facet & all their selectors can be frozen
/// @param selectors An array of unique selectors that belongs to the facet address
struct FacetCut {
address facet;
Action action;
bool isFreezable;
bytes4[] selectors;
}
/// @dev Structure of the diamond proxy changes
/// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts
/// @param initAddress The address that's delegate called after setting up new facet changes
/// @param initCalldata Calldata for the delegate call to `initAddress`
struct DiamondCutData {
FacetCut[] facetCuts;
address initAddress;
bytes initCalldata;
}
/// @dev Type of change over diamond: add/replace/remove facets
enum Action {
Add,
Replace,
Remove
}
/// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored
function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
diamondStorage.slot := position
}
}
/// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall
/// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall
function diamondCut(DiamondCutData memory _diamondCut) internal {
FacetCut[] memory facetCuts = _diamondCut.facetCuts;
address initAddress = _diamondCut.initAddress;
bytes memory initCalldata = _diamondCut.initCalldata;
uint256 facetCutsLength = facetCuts.length;
for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) {
Action action = facetCuts[i].action;
address facet = facetCuts[i].facet;
bool isFacetFreezable = facetCuts[i].isFreezable;
bytes4[] memory selectors = facetCuts[i].selectors;
require(selectors.length > 0, "B"); // no functions for diamond cut
if (action == Action.Add) {
_addFunctions(facet, selectors, isFacetFreezable);
} else if (action == Action.Replace) {
_replaceFunctions(facet, selectors, isFacetFreezable);
} else if (action == Action.Remove) {
_removeFunctions(facet, selectors);
} else {
revert("C"); // undefined diamond cut action
}
}
_initializeDiamondCut(initAddress, initCalldata);
emit DiamondCut(facetCuts, initAddress, initCalldata);
}
/// @dev Add new functions to the diamond proxy
/// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
function _addFunctions(
address _facet,
bytes4[] memory _selectors,
bool _isFacetFreezable
) private {
DiamondStorage storage ds = getDiamondStorage();
require(_facet != address(0), "G"); // facet with zero address cannot be added
// Add facet to the list of facets if the facet address is new one
_saveFacetIfNew(_facet);
uint256 selectorsLength = _selectors.length;
for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
bytes4 selector = _selectors[i];
SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists
_addOneFunction(_facet, selector, _isFacetFreezable);
}
}
/// @dev Change associated facets to already known function selectors
/// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
function _replaceFunctions(
address _facet,
bytes4[] memory _selectors,
bool _isFacetFreezable
) private {
DiamondStorage storage ds = getDiamondStorage();
require(_facet != address(0), "K"); // cannot replace facet with zero address
uint256 selectorsLength = _selectors.length;
for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
bytes4 selector = _selectors[i];
SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address
_removeOneFunction(oldFacet.facetAddress, selector);
// Add facet to the list of facets if the facet address is a new one
_saveFacetIfNew(_facet);
_addOneFunction(_facet, selector, _isFacetFreezable);
}
}
/// @dev Remove association with function and facet
/// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
function _removeFunctions(address _facet, bytes4[] memory _selectors) private {
DiamondStorage storage ds = getDiamondStorage();
require(_facet == address(0), "a1"); // facet address must be zero
uint256 selectorsLength = _selectors.length;
for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
bytes4 selector = _selectors[i];
SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet
_removeOneFunction(oldFacet.facetAddress, selector);
}
}
/// @dev Add address to the list of known facets if it is not on the list yet
/// NOTE: should be called ONLY before adding a new selector associated with the address
function _saveFacetIfNew(address _facet) private {
DiamondStorage storage ds = getDiamondStorage();
uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length;
// If there are no selectors associated with facet then save facet as new one
if (selectorsLength == 0) {
ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16();
ds.facets.push(_facet);
}
}
/// @dev Add one function to the already known facet
/// NOTE: It is expected but NOT enforced that:
/// - `_facet` is NON-ZERO address
/// - `_facet` is already stored address in `DiamondStorage.facets`
/// - `_selector` is NOT associated by another facet
function _addOneFunction(
address _facet,
bytes4 _selector,
bool _isSelectorFreezable
) private {
DiamondStorage storage ds = getDiamondStorage();
uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
// if selectorPosition is nonzero, it means it is not a new facet
// so the freezability of the first selector must be matched to _isSelectorFreezable
// so all the selectors in a facet will have the same freezability
if (selectorPosition != 0) {
bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0];
require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1");
}
ds.selectorToFacet[_selector] = SelectorToFacet({
facetAddress: _facet,
selectorPosition: selectorPosition,
isFreezable: _isSelectorFreezable
});
ds.facetToSelectors[_facet].selectors.push(_selector);
}
/// @dev Remove one associated function with facet
/// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address
function _removeOneFunction(address _facet, bytes4 _selector) private {
DiamondStorage storage ds = getDiamondStorage();
// Get index of `FacetToSelectors.selectors` of the selector and last element of array
uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition;
uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1;
// If the selector is not at the end of the array then move the last element to the selector position
if (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition];
ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector;
ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16();
}
// Remove last element from the selectors array
ds.facetToSelectors[_facet].selectors.pop();
// Finally, clean up the association with facet
delete ds.selectorToFacet[_selector];
// If there are no selectors for facet then remove the facet from the list of known facets
if (lastSelectorPosition == 0) {
_removeFacet(_facet);
}
}
/// @dev remove facet from the list of known facets
/// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet`
function _removeFacet(address _facet) private {
DiamondStorage storage ds = getDiamondStorage();
// Get index of `DiamondStorage.facets` of the facet and last element of array
uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition;
uint256 lastFacetPosition = ds.facets.length - 1;
// If the facet is not at the end of the array then move the last element to the facet position
if (facetPosition != lastFacetPosition) {
address lastFacet = ds.facets[lastFacetPosition];
ds.facets[facetPosition] = lastFacet;
ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16();
}
// Remove last element from the facets array
ds.facets.pop();
}
/// @dev Delegates call to the initialization address with provided calldata
/// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets
function _initializeDiamondCut(address _init, bytes memory _calldata) private {
if (_init == address(0)) {
require(_calldata.length == 0, "H"); // Non-empty calldata for zero address
} else {
// Do not check whether `_init` is a contract since later we check that it returns data.
(bool success, bytes memory data) = _init.delegatecall(_calldata);
require(success, "I"); // delegatecall failed
// Check that called contract returns magic value to make sure that contract logic
// supposed to be used as diamond cut initializer.
require(data.length == 32, "lp");
require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
}
}
}
File 3 of 3: ExecutorFacet
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./interfaces/IAllowList.sol";
/// @author Matter Labs
abstract contract AllowListed {
modifier senderCanCallFunction(IAllowList _allowList) {
// Preventing the stack too deep error
{
require(_allowList.canCall(msg.sender, address(this), msg.sig), "nr");
}
_;
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
interface IAllowList {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Access mode of target contract is changed
event UpdateAccessMode(address indexed target, AccessMode previousMode, AccessMode newMode);
/// @notice Permission to call is changed
event UpdateCallPermission(address indexed caller, address indexed target, bytes4 indexed functionSig, bool status);
/// @notice Type of access to a specific contract includes three different modes
/// @param Closed No one has access to the contract
/// @param SpecialAccessOnly Any address with granted special access can interact with a contract (see `hasSpecialAccessToCall`)
/// @param Public Everyone can interact with a contract
enum AccessMode {
Closed,
SpecialAccessOnly,
Public
}
/// @dev A struct that contains deposit limit data of a token
/// @param depositLimitation Whether any deposit limitation is placed or not
/// @param depositCap The maximum amount that can be deposited.
struct Deposit {
bool depositLimitation;
uint256 depositCap;
}
/*//////////////////////////////////////////////////////////////
GETTERS
//////////////////////////////////////////////////////////////*/
function getAccessMode(address _target) external view returns (AccessMode);
function hasSpecialAccessToCall(
address _caller,
address _target,
bytes4 _functionSig
) external view returns (bool);
function canCall(
address _caller,
address _target,
bytes4 _functionSig
) external view returns (bool);
function getTokenDepositLimitData(address _l1Token) external view returns (Deposit memory);
/*//////////////////////////////////////////////////////////////
ALLOW LIST LOGIC
//////////////////////////////////////////////////////////////*/
function setBatchAccessMode(address[] calldata _targets, AccessMode[] calldata _accessMode) external;
function setAccessMode(address _target, AccessMode _accessMode) external;
function setBatchPermissionToCall(
address[] calldata _callers,
address[] calldata _targets,
bytes4[] calldata _functionSigs,
bool[] calldata _enables
) external;
function setPermissionToCall(
address _caller,
address _target,
bytes4 _functionSig,
bool _enable
) external;
/*//////////////////////////////////////////////////////////////
DEPOSIT LIMIT LOGIC
//////////////////////////////////////////////////////////////*/
function setDepositLimit(
address _l1Token,
bool _depositLimitation,
uint256 _depositCap
) external;
}
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
/// @dev The address of the L2 deployer system contract.
address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006);
/// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed bytecode.
/// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
/// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer system contract
/// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the `diamond-initializers` contracts.
address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007);
/// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008);
/// @dev The formal address of the initial program of the system: the bootloader
address constant L2_BOOTLOADER_ADDRESS = address(0x8001);
/// @dev The address of the eth token system contract
address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a);
/// @dev The address of the known code storage system contract
address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004);
/// @dev The address of the context system contract
address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b);
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
/**
* @author Matter Labs
* @notice Helper library for working with L2 contracts on L1.
*/
library L2ContractHelper {
/// @dev The prefix used to create CREATE2 addresses.
bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2");
/// @notice Validate the bytecode format and calculate its hash.
/// @param _bytecode The bytecode to hash.
/// @return hashedBytecode The 32-byte hash of the bytecode.
/// Note: The function reverts the execution if the bytecode has non expected format:
/// - Bytecode bytes length is not a multiple of 32
/// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
/// - Bytecode words length is not odd
function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
// Note that the length of the bytecode must be provided in 32-byte words.
require(_bytecode.length % 32 == 0, "po");
uint256 bytecodeLenInWords = _bytecode.length / 32;
require(bytecodeLenInWords < 2**16, "pp"); // bytecode length must be less than 2^16 words
require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd
hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
// Setting the version of the hash
hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
// Setting the length
hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
}
/// @notice Validates the format of the given bytecode hash.
/// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash.
/// @dev The function reverts on invalid bytecode hash formam.
/// @param _bytecodeHash The hash of the bytecode to validate.
function validateBytecodeHash(bytes32 _bytecodeHash) internal pure {
uint8 version = uint8(_bytecodeHash[0]);
require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash
require(_bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd
}
/// @notice Returns the length of the bytecode associated with the given hash.
/// @param _bytecodeHash The hash of the bytecode.
/// @return codeLengthInWords The length of the bytecode in words.
function _bytecodeLen(bytes32 _bytecodeHash) private pure returns (uint256 codeLengthInWords) {
codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3]));
}
/// @notice Computes the create2 address for a Layer 2 contract.
/// @param _sender The address of the sender.
/// @param _salt The salt value to use in the create2 address computation.
/// @param _bytecodeHash The contract bytecode hash.
/// @param _constructorInputHash The hash of the constructor input data.
/// @return The create2 address of the contract.
/// NOTE: L2 create2 derivation is different from L1 derivation!
function computeCreate2Address(
address _sender,
bytes32 _salt,
bytes32 _bytecodeHash,
bytes32 _constructorInputHash
) internal pure returns (address) {
bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
bytes32 data = keccak256(
bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash)
);
return address(uint160(uint256(data)));
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
library UncheckedMath {
function uncheckedInc(uint256 _number) internal pure returns (uint256) {
unchecked {
return _number + 1;
}
}
function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
unchecked {
return _lhs + _rhs;
}
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
/**
* @author Matter Labs
* @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array.
* @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type.
*
* @dev WARNING!
* 1) Functions don't check the length of the bytes array, so it can go out of bounds.
* The user of the library must check for bytes length before using any functions from the library!
*
* 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html.
* Using data in inline assembly can lead to unexpected behavior!
*/
library UnsafeBytes {
function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) {
assembly {
offset := add(_start, 4)
result := mload(add(_bytes, offset))
}
}
function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) {
assembly {
offset := add(_start, 20)
result := mload(add(_bytes, offset))
}
}
function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) {
assembly {
offset := add(_start, 32)
result := mload(add(_bytes, offset))
}
}
function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) {
assembly {
offset := add(_start, 32)
result := mload(add(_bytes, offset))
}
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
abstract contract ReentrancyGuard {
/// @dev Address of lock flag variable.
/// @dev Flag is placed at random memory location to not interfere with Storage contract.
uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
modifier reentrancyGuardInitializer() {
_initializeReentrancyGuard();
_;
}
function _initializeReentrancyGuard() private {
uint256 lockSlotOldValue;
// Storing an initial non-zero value makes deployment a bit more
// expensive but in exchange every call to nonReentrant
// will be cheaper.
assembly {
lockSlotOldValue := sload(LOCK_FLAG_ADDRESS)
sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
}
// Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict
require(lockSlotOldValue == 0, "1B");
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
uint256 _status;
assembly {
_status := sload(LOCK_FLAG_ADDRESS)
}
// On the first call to nonReentrant, _notEntered will be true
require(_status == _NOT_ENTERED, "r1");
// Any calls to nonReentrant after this point will fail
assembly {
sstore(LOCK_FLAG_ADDRESS, _ENTERED)
}
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
assembly {
sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
}
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
/// @dev `keccak256("")`
bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
/// @dev Bytes in raw L2 log
/// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBlock, address sender, bytes32 key, bytes32 value)
uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
/// @dev The maximum length of the bytes array with L2 -> L1 logs
uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;
/// @dev L2 -> L1 logs Merkle tree height
uint256 constant L2_TO_L1_LOG_MERKLE_TREE_HEIGHT = 9;
/// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree
/// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
/// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
/// @dev Number of bytes in a one initial storage change
/// @dev Equal to the bytes size of the tuple - (bytes32 key, bytes32 value)
uint256 constant INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE = 64;
/// @dev The maximum length of the bytes array with initial storage changes
uint256 constant MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE * 4765;
/// @dev Number of bytes in a one repeated storage change
/// @dev Equal to the bytes size of the tuple - (bytes8 key, bytes32 value)
uint256 constant REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE = 40;
/// @dev The maximum length of the bytes array with repeated storage changes
uint256 constant MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE * 7564;
// TODO: change constant to the real root hash of empty Merkle tree (SMA-184)
bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0);
/// @dev Denotes the first byte of the zkSync transaction that came from L1.
uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
/// @dev The amount of time in seconds the validator has to process the priority transaction
/// NOTE: The constant is set to zero for the Alpha release period
uint256 constant PRIORITY_EXPIRATION = 0 days;
/// @dev Notice period before activation preparation status of upgrade mode (in seconds)
/// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.
uint256 constant UPGRADE_NOTICE_PERIOD = 0;
/// @dev Timestamp - seconds since unix epoch
uint256 constant COMMIT_TIMESTAMP_NOT_OLDER = 365 days;
/// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds)
/// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds)
uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 365 days;
/// @dev Bit mask to apply for verifier public input before verifying.
uint256 constant INPUT_MASK = 452312848583266388373324160190187140051835877600158453279131187530910662655;
/// @dev The maximum number of L2 gas that a user can request for an L2 transaction
uint256 constant L2_TX_MAX_GAS_LIMIT = 80000000;
/// @dev The maximum number of the pubdata an L2 operation should be allowed to use.
uint256 constant MAX_PUBDATA_PER_BLOCK = 110000;
/// @dev The maximum number of the pubdata an priority operation should be allowed to use.
/// For now, it is somewhat lower than the maximum number of pubdata allowed for an L2 transaction,
/// to ensure that the transaction is definitely processable on L2 despite any potential overhead.
uint256 constant PRIORITY_TX_MAX_PUBDATA = 99000;
/// @dev The default price per L2 gas to be used for L1->L2 transactions
uint256 constant FAIR_L2_GAS_PRICE = 500000000;
/// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased
/// value.
uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17;
/// @dev The computational overhead of processing an L2 block.
uint256 constant BLOCK_OVERHEAD_L2_GAS = 1200000;
/// @dev The overhead in L1 gas of interacting with the L1
uint256 constant BLOCK_OVERHEAD_L1_GAS = 1000000;
/// @dev The equivalent in L1 pubdata of L1 gas used for working with L1
uint256 constant BLOCK_OVERHEAD_PUBDATA = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE;
/// @dev The maximum number of transactions in L2 block:
uint256 constant MAX_TRANSACTIONS_IN_BLOCK = 1024;
/// @dev The size of the bootloader memory dedicated to the encodings of transactions
uint256 constant BOOTLOADER_TX_ENCODING_SPACE = 485225;
/// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas
uint256 constant L1_TX_INTRINSIC_L2_GAS = 167157;
/// @dev The intrinsic cost of the L1->l2 transaction in pubdata
uint256 constant L1_TX_INTRINSIC_PUBDATA = 88;
/// @dev The minimal base price for L1 transaction
uint256 constant L1_TX_MIN_L2_GAS_BASE = 173484;
/// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding
uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656;
/// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473;
/// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64;
/// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency
uint256 constant MAX_NEW_FACTORY_DEPS = 32;
/// @dev The L2 gasPricePerPubdata required to be used in bridges.
uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800;
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "../Storage.sol";
import "../../common/ReentrancyGuard.sol";
import "../../common/AllowListed.sol";
/// @title Base contract containing functions accessible to the other facets.
/// @author Matter Labs
contract Base is ReentrancyGuard, AllowListed {
AppStorage internal s;
/// @notice Checks that the message sender is an active governor
modifier onlyGovernor() {
require(msg.sender == s.governor, "1g"); // only by governor
_;
}
/// @notice Checks if validator is active
modifier onlyValidator() {
require(s.validators[msg.sender], "1h"); // validator is not active
_;
}
modifier onlySecurityCouncil() {
require(msg.sender == s.upgrades.securityCouncil, "a9"); // not a security council
_;
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./Base.sol";
import "../Config.sol";
import "../interfaces/IExecutor.sol";
import "../libraries/PairingsBn254.sol";
import "../libraries/PriorityQueue.sol";
import "../../common/libraries/UncheckedMath.sol";
import "../../common/libraries/UnsafeBytes.sol";
import "../../common/libraries/L2ContractHelper.sol";
import "../../common/L2ContractAddresses.sol";
/// @title zkSync Executor contract capable of processing events emitted in the zkSync protocol.
/// @author Matter Labs
contract ExecutorFacet is Base, IExecutor {
using UncheckedMath for uint256;
using PriorityQueue for PriorityQueue.Queue;
/// @dev Process one block commit using the previous block StoredBlockInfo
/// @dev returns new block StoredBlockInfo
/// @notice Does not change storage
function _commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo calldata _newBlock)
internal
view
returns (StoredBlockInfo memory)
{
require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block
// Check that block contain all meta information for L2 logs.
// Get the chained hash of priority transaction hashes.
(
uint256 expectedNumberOfLayer1Txs,
bytes32 expectedPriorityOperationsHash,
bytes32 previousBlockHash,
uint256 l2BlockTimestamp
) = _processL2Logs(_newBlock);
require(_previousBlock.blockHash == previousBlockHash, "l");
// Check that the priority operation hash in the L2 logs is as expected
require(expectedPriorityOperationsHash == _newBlock.priorityOperationsHash, "t");
// Check that the number of processed priority operations is as expected
require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs, "ta");
// Check that the timestamp that came from the Bootloader is expected
require(l2BlockTimestamp == _newBlock.timestamp, "tb");
// Preventing "stack too deep error"
{
// Check the timestamp of the new block
bool timestampNotTooSmall = block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= l2BlockTimestamp;
bool timestampNotTooBig = l2BlockTimestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA;
require(timestampNotTooSmall, "h"); // New block timestamp is too small
require(timestampNotTooBig, "h1"); // New block timestamp is too big
// Check the index of repeated storage writes
uint256 newStorageChangesIndexes = uint256(uint32(bytes4(_newBlock.initialStorageChanges[:4])));
require(
_previousBlock.indexRepeatedStorageChanges + newStorageChangesIndexes ==
_newBlock.indexRepeatedStorageChanges,
"yq"
);
// NOTE: We don't check that _newBlock.timestamp > _previousBlock.timestamp, it is checked inside the L2
}
// Create block commitment for the proof verification
bytes32 commitment = _createBlockCommitment(_newBlock);
return
StoredBlockInfo(
_newBlock.blockNumber,
_newBlock.newStateRoot,
_newBlock.indexRepeatedStorageChanges,
_newBlock.numberOfLayer1Txs,
_newBlock.priorityOperationsHash,
_newBlock.l2LogsTreeRoot,
_newBlock.timestamp,
commitment
);
}
/// @dev Check that L2 logs are proper and block contain all meta information for them
function _processL2Logs(CommitBlockInfo calldata _newBlock)
internal
pure
returns (
uint256 numberOfLayer1Txs,
bytes32 chainedPriorityTxsHash,
bytes32 previousBlockHash,
uint256 blockTimestamp
)
{
// Copy L2 to L1 logs into memory.
bytes memory emittedL2Logs = _newBlock.l2Logs[4:];
bytes[] calldata l2Messages = _newBlock.l2ArbitraryLengthMessages;
uint256 currentMessage;
// Auxiliary variable that is needed to enforce that `previousBlockHash` and `blockTimestamp` was read exactly one time
bool isSystemContextLogProcessed;
bytes[] calldata factoryDeps = _newBlock.factoryDeps;
uint256 currentBytecode;
chainedPriorityTxsHash = EMPTY_STRING_KECCAK;
// linear traversal of the logs
for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {
(address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + 4);
// show preimage for hashed message stored in log
if (logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
(bytes32 hashedMessage, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
require(keccak256(l2Messages[currentMessage]) == hashedMessage, "k2");
currentMessage = currentMessage.uncheckedInc();
} else if (logSender == L2_BOOTLOADER_ADDRESS) {
(bytes32 canonicalTxHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
chainedPriorityTxsHash = keccak256(abi.encode(chainedPriorityTxsHash, canonicalTxHash));
// Overflow is not realistic
numberOfLayer1Txs = numberOfLayer1Txs.uncheckedInc();
} else if (logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) {
// Make sure that the system context log wasn't processed yet, to
// avoid accident double reading `blockTimestamp` and `previousBlockHash`
require(!isSystemContextLogProcessed, "fx");
(blockTimestamp, ) = UnsafeBytes.readUint256(emittedL2Logs, i + 24);
(previousBlockHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
// Mark system context log as processed
isSystemContextLogProcessed = true;
} else if (logSender == L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR) {
(bytes32 bytecodeHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
require(bytecodeHash == L2ContractHelper.hashL2Bytecode(factoryDeps[currentBytecode]), "k3");
currentBytecode = currentBytecode.uncheckedInc();
}
}
// To check that only relevant preimages have been included in the calldata
require(currentBytecode == factoryDeps.length, "ym");
require(currentMessage == l2Messages.length, "pl");
// `blockTimestamp` and `previousBlockHash` wasn't read from L2 logs
require(isSystemContextLogProcessed, "by");
}
/// @notice Commit block
/// @notice 1. Checks timestamp.
/// @notice 2. Process L2 logs.
/// @notice 3. Store block commitments.
function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
external
override
nonReentrant
onlyValidator
{
// Check that we commit blocks after last committed block
require(s.storedBlockHashes[s.totalBlocksCommitted] == _hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data
uint256 blocksLength = _newBlocksData.length;
for (uint256 i = 0; i < blocksLength; i = i.uncheckedInc()) {
_lastCommittedBlockData = _commitOneBlock(_lastCommittedBlockData, _newBlocksData[i]);
s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData);
emit BlockCommit(
_lastCommittedBlockData.blockNumber,
_lastCommittedBlockData.blockHash,
_lastCommittedBlockData.commitment
);
}
s.totalBlocksCommitted = s.totalBlocksCommitted + blocksLength;
}
/// @dev Pops the priority operations from the priority queue and returns a rolling hash of operations
function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) {
concatHash = EMPTY_STRING_KECCAK;
for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) {
PriorityOperation memory priorityOp = s.priorityQueue.popFront();
concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash));
}
}
/// @dev Executes one block
/// @dev 1. Processes all pending operations (Complete priority requests)
/// @dev 2. Finalizes block on Ethereum
/// @dev _executedBlockIdx is an index in the array of the blocks that we want to execute together
function _executeOneBlock(StoredBlockInfo memory _storedBlock, uint256 _executedBlockIdx) internal {
uint256 currentBlockNumber = _storedBlock.blockNumber;
require(currentBlockNumber == s.totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order
require(
_hashStoredBlockInfo(_storedBlock) == s.storedBlockHashes[currentBlockNumber],
"exe10" // executing block should be committed
);
bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBlock.numberOfLayer1Txs);
require(priorityOperationsHash == _storedBlock.priorityOperationsHash, "x"); // priority operations hash does not match to expected
// Save root hash of L2 -> L1 logs tree
s.l2LogsRootHashes[currentBlockNumber] = _storedBlock.l2LogsTreeRoot;
}
/// @notice Execute blocks, complete priority operations and process withdrawals.
/// @notice 1. Processes all pending operations (Complete priority requests)
/// @notice 2. Finalizes block on Ethereum
function executeBlocks(StoredBlockInfo[] calldata _blocksData) external nonReentrant onlyValidator {
uint256 nBlocks = _blocksData.length;
for (uint256 i = 0; i < nBlocks; i = i.uncheckedInc()) {
_executeOneBlock(_blocksData[i], i);
emit BlockExecution(_blocksData[i].blockNumber, _blocksData[i].blockHash, _blocksData[i].commitment);
}
s.totalBlocksExecuted = s.totalBlocksExecuted + nBlocks;
require(s.totalBlocksExecuted <= s.totalBlocksVerified, "n"); // Can't execute blocks more than committed and proven currently.
}
/// @notice Blocks commitment verification.
/// @notice Only verifies block commitments without any other processing
function proveBlocks(
StoredBlockInfo calldata _prevBlock,
StoredBlockInfo[] calldata _committedBlocks,
ProofInput calldata _proof
) external nonReentrant onlyValidator {
// Save the variables into the stack to save gas on reading them later
uint256 currentTotalBlocksVerified = s.totalBlocksVerified;
uint256 committedBlocksLength = _committedBlocks.length;
// Save the variable from the storage to memory to save gas
VerifierParams memory verifierParams = s.verifierParams;
// Initialize the array, that will be used as public input to the ZKP
uint256[] memory proofPublicInput = new uint256[](committedBlocksLength);
// Check that the block passed by the validator is indeed the first unverified block
require(_hashStoredBlockInfo(_prevBlock) == s.storedBlockHashes[currentTotalBlocksVerified], "t1");
bytes32 prevBlockCommitment = _prevBlock.commitment;
for (uint256 i = 0; i < committedBlocksLength; i = i.uncheckedInc()) {
currentTotalBlocksVerified = currentTotalBlocksVerified.uncheckedInc();
require(_hashStoredBlockInfo(_committedBlocks[i]) == s.storedBlockHashes[currentTotalBlocksVerified], "o1");
bytes32 currentBlockCommitment = _committedBlocks[i].commitment;
proofPublicInput[i] = _getBlockProofPublicInput(
prevBlockCommitment,
currentBlockCommitment,
_proof,
verifierParams
);
prevBlockCommitment = currentBlockCommitment;
}
require(currentTotalBlocksVerified <= s.totalBlocksCommitted, "q");
bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof);
require(successVerifyProof, "p"); // Proof verification fail
// Verify the recursive part that was given to us through the public input
bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput);
require(successProofAggregation, "hh"); // Proof aggregation must be valid
emit BlocksVerification(s.totalBlocksVerified, currentTotalBlocksVerified);
s.totalBlocksVerified = currentTotalBlocksVerified;
}
/// @dev Gets zk proof public input
function _getBlockProofPublicInput(
bytes32 _prevBlockCommitment,
bytes32 _currentBlockCommitment,
ProofInput calldata _proof,
VerifierParams memory _verifierParams
) internal pure returns (uint256) {
return
uint256(
keccak256(
abi.encodePacked(
_prevBlockCommitment,
_currentBlockCommitment,
_verifierParams.recursionNodeLevelVkHash,
_verifierParams.recursionLeafLevelVkHash,
_verifierParams.recursionCircuitsSetVksHash,
_proof.recursiveAggregationInput
)
)
) & INPUT_MASK;
}
/// @dev Verify a part of the zkp, that is responsible for the aggregation
function _verifyRecursivePartOfProof(uint256[] calldata _recursiveAggregationInput) internal view returns (bool) {
require(_recursiveAggregationInput.length == 4, "vr");
PairingsBn254.G1Point memory pairWithGen = PairingsBn254.new_g1_checked(
_recursiveAggregationInput[0],
_recursiveAggregationInput[1]
);
PairingsBn254.G1Point memory pairWithX = PairingsBn254.new_g1_checked(
_recursiveAggregationInput[2],
_recursiveAggregationInput[3]
);
PairingsBn254.G2Point memory g2Gen = PairingsBn254.new_g2(
[
0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
],
[
0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
]
);
PairingsBn254.G2Point memory g2X = PairingsBn254.new_g2(
[
0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
],
[
0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
]
);
return PairingsBn254.pairingProd2(pairWithGen, g2Gen, pairWithX, g2X);
}
/// @notice Reverts unexecuted blocks
/// @param _newLastBlock block number after which blocks should be reverted
/// NOTE: Doesn't delete the stored data about blocks, but only decreases
/// counters that are responsible for the number of blocks
function revertBlocks(uint256 _newLastBlock) external nonReentrant onlyValidator {
require(s.totalBlocksCommitted > _newLastBlock, "v1"); // The last committed block is less than new last block
uint256 newTotalBlocksCommitted = _maxU256(_newLastBlock, s.totalBlocksExecuted);
if (newTotalBlocksCommitted < s.totalBlocksVerified) {
s.totalBlocksVerified = newTotalBlocksCommitted;
}
s.totalBlocksCommitted = newTotalBlocksCommitted;
emit BlocksRevert(s.totalBlocksCommitted, s.totalBlocksVerified, s.totalBlocksExecuted);
}
/// @notice Returns larger of two values
function _maxU256(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? b : a;
}
/// @dev Creates block commitment from its data
function _createBlockCommitment(CommitBlockInfo calldata _newBlockData) internal view returns (bytes32) {
bytes32 passThroughDataHash = keccak256(_blockPassThroughData(_newBlockData));
bytes32 metadataHash = keccak256(_blockMetaParameters());
bytes32 auxiliaryOutputHash = keccak256(_blockAuxiliaryOutput(_newBlockData));
return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash));
}
function _blockPassThroughData(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
return
abi.encodePacked(
_block.indexRepeatedStorageChanges,
_block.newStateRoot,
uint64(0), // index repeated storage changes in zkPorter
bytes32(0) // zkPorter block hash
);
}
function _blockMetaParameters() internal view returns (bytes memory) {
return abi.encodePacked(s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, s.l2DefaultAccountBytecodeHash);
}
function _blockAuxiliaryOutput(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
require(_block.initialStorageChanges.length <= MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, "pf");
require(_block.repeatedStorageChanges.length <= MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, "py");
require(_block.l2Logs.length <= MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, "pu");
bytes32 initialStorageChangesHash = keccak256(_block.initialStorageChanges);
bytes32 repeatedStorageChangesHash = keccak256(_block.repeatedStorageChanges);
bytes32 l2ToL1LogsHash = keccak256(_block.l2Logs);
return abi.encode(_block.l2LogsTreeRoot, l2ToL1LogsHash, initialStorageChangesHash, repeatedStorageChangesHash);
}
/// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
function _hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
return keccak256(abi.encode(_storedBlockInfo));
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
interface IExecutor {
/// @notice Rollup block stored data
/// @param blockNumber Rollup block number
/// @param blockHash Hash of L2 block
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param priorityOperationsHash Hash of all priority operations from this block
/// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
/// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
/// @param commitment Verified input for the zkSync circuit
struct StoredBlockInfo {
uint64 blockNumber;
bytes32 blockHash;
uint64 indexRepeatedStorageChanges;
uint256 numberOfLayer1Txs;
bytes32 priorityOperationsHash;
bytes32 l2LogsTreeRoot;
uint256 timestamp;
bytes32 commitment;
}
/// @notice Data needed to commit new block
/// @param blockNumber Number of the committed block
/// @param timestamp Unix timestamp denoting the start of the block execution
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param newStateRoot The state root of the full state tree
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
/// @param priorityOperationsHash Hash of all priority operations from this block
/// @param initialStorageChanges Storage write access as a concatenation key-value
/// @param repeatedStorageChanges Storage write access as a concatenation index-value
/// @param l2Logs concatenation of all L2 -> L1 logs in the block
/// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
/// @param factoryDeps array of l2 bytecodes that were marked as known on L2
struct CommitBlockInfo {
uint64 blockNumber;
uint64 timestamp;
uint64 indexRepeatedStorageChanges;
bytes32 newStateRoot;
uint256 numberOfLayer1Txs;
bytes32 l2LogsTreeRoot;
bytes32 priorityOperationsHash;
bytes initialStorageChanges;
bytes repeatedStorageChanges;
bytes l2Logs;
bytes[] l2ArbitraryLengthMessages;
bytes[] factoryDeps;
}
/// @notice Recursive proof input data (individual commitments are constructed onchain)
struct ProofInput {
uint256[] recursiveAggregationInput;
uint256[] serializedProof;
}
function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
external;
function proveBlocks(
StoredBlockInfo calldata _prevBlock,
StoredBlockInfo[] calldata _committedBlocks,
ProofInput calldata _proof
) external;
function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
function revertBlocks(uint256 _newLastBlock) external;
/// @notice Event emitted when a block is committed
event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
/// @notice Event emitted when blocks are verified
event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
/// @notice Event emitted when a block is executed
event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
/// @notice Event emitted when blocks are reverted
event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
library PairingsBn254 {
uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant bn254_b_coeff = 3;
struct G1Point {
uint256 X;
uint256 Y;
}
struct Fr {
uint256 value;
}
function new_fr(uint256 fr) internal pure returns (Fr memory) {
require(fr < r_mod);
return Fr({value: fr});
}
function copy(Fr memory self) internal pure returns (Fr memory n) {
n.value = self.value;
}
function assign(Fr memory self, Fr memory other) internal pure {
self.value = other.value;
}
function inverse(Fr memory fr) internal view returns (Fr memory) {
require(fr.value != 0);
return pow(fr, r_mod - 2);
}
function add_assign(Fr memory self, Fr memory other) internal pure {
self.value = addmod(self.value, other.value, r_mod);
}
function sub_assign(Fr memory self, Fr memory other) internal pure {
self.value = addmod(self.value, r_mod - other.value, r_mod);
}
function mul_assign(Fr memory self, Fr memory other) internal pure {
self.value = mulmod(self.value, other.value, r_mod);
}
function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
uint256[1] memory result;
bool success;
assembly {
success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
}
require(success);
return Fr({value: result[0]});
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
return G1Point(x, y);
}
// function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
if (x == 0 && y == 0) {
// point of infinity is (0,0)
return G1Point(x, y);
}
// check encoding
require(x < q_mod, "x axis isn't valid");
require(y < q_mod, "y axis isn't valid");
// check on curve
uint256 lhs = mulmod(y, y, q_mod); // y^2
uint256 rhs = mulmod(x, x, q_mod); // x^2
rhs = mulmod(rhs, x, q_mod); // x^3
rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
require(lhs == rhs, "is not on curve");
return G1Point(x, y);
}
function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
return G2Point(x, y);
}
function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
result.X = self.X;
result.Y = self.Y;
}
function P2() internal pure returns (G2Point memory) {
// for some reason ethereum expects to have c1*v + c0 form
return
G2Point(
[
0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
],
[
0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
]
);
}
function negate(G1Point memory self) internal pure {
// The prime q in the base field F_q for G1
if (self.Y == 0) {
require(self.X == 0);
return;
}
self.Y = q_mod - self.Y;
}
function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
point_add_into_dest(p1, p2, r);
return r;
}
function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
point_add_into_dest(p1, p2, p1);
}
function point_add_into_dest(
G1Point memory p1,
G1Point memory p2,
G1Point memory dest
) internal view {
if (p2.X == 0 && p2.Y == 0) {
// we add zero, nothing happens
dest.X = p1.X;
dest.Y = p1.Y;
return;
} else if (p1.X == 0 && p1.Y == 0) {
// we add into zero, and we add non-zero point
dest.X = p2.X;
dest.Y = p2.Y;
return;
} else {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
assembly {
success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
}
require(success);
}
}
function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
point_sub_into_dest(p1, p2, p1);
}
function point_sub_into_dest(
G1Point memory p1,
G1Point memory p2,
G1Point memory dest
) internal view {
if (p2.X == 0 && p2.Y == 0) {
// we subtracted zero, nothing happens
dest.X = p1.X;
dest.Y = p1.Y;
return;
} else if (p1.X == 0 && p1.Y == 0) {
// we subtract from zero, and we subtract non-zero point
dest.X = p2.X;
dest.Y = q_mod - p2.Y;
return;
} else {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = q_mod - p2.Y;
bool success = false;
assembly {
success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
}
require(success);
}
}
function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
// https://eips.ethereum.org/EIPS/eip-197
// Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0)
if (p.X == 0 && p.Y == 1) {
p.Y = 0;
}
point_mul_into_dest(p, s, r);
return r;
}
function point_mul_assign(G1Point memory p, Fr memory s) internal view {
point_mul_into_dest(p, s, p);
}
function point_mul_into_dest(
G1Point memory p,
Fr memory s,
G1Point memory dest
) internal view {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s.value;
bool success;
assembly {
success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
}
require(success);
}
function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length);
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < elements; ) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 5] = p2[i].Y[1];
unchecked {
++i;
}
}
uint256[1] memory out;
bool success;
assembly {
success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
}
require(success);
return out[0] != 0;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
/// @notice The structure that contains meta information of the L2 transaction that was requested from L1
/// @dev The weird size of fields was selected specifically to minimize the structure storage size
/// @param canonicalTxHash Hashed L2 transaction data that is needed to process it
/// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before)
/// @param layer2Tip Additional payment to the validator as an incentive to perform the operation
struct PriorityOperation {
bytes32 canonicalTxHash;
uint64 expirationTimestamp;
uint192 layer2Tip;
}
/// @author Matter Labs
/// @dev The library provides the API to interact with the priority queue container
/// @dev Order of processing operations from queue - FIFO (Fist in - first out)
library PriorityQueue {
using PriorityQueue for Queue;
/// @notice Container that stores priority operations
/// @param data The inner mapping that saves priority operation by its index
/// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty
/// @param tail The pointer to the free slot
struct Queue {
mapping(uint256 => PriorityOperation) data;
uint256 tail;
uint256 head;
}
/// @notice Returns zero if and only if no operations were processed from the queue
/// @return Index of the oldest priority operation that wasn't processed yet
function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) {
return _queue.head;
}
/// @return The total number of priority operations that were added to the priority queue, including all processed ones
function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) {
return _queue.tail;
}
/// @return The total number of unprocessed priority operations in a priority queue
function getSize(Queue storage _queue) internal view returns (uint256) {
return uint256(_queue.tail - _queue.head);
}
/// @return Whether the priority queue contains no operations
function isEmpty(Queue storage _queue) internal view returns (bool) {
return _queue.tail == _queue.head;
}
/// @notice Add the priority operation to the end of the priority queue
function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
// Save value into the stack to avoid double reading from the storage
uint256 tail = _queue.tail;
_queue.data[tail] = _operation;
_queue.tail = tail + 1;
}
/// @return The first unprocessed priority operation from the queue
function front(Queue storage _queue) internal view returns (PriorityOperation memory) {
require(!_queue.isEmpty(), "D"); // priority queue is empty
return _queue.data[_queue.head];
}
/// @notice Remove the first unprocessed priority operation from the queue
/// @return priorityOperation that was popped from the priority queue
function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) {
require(!_queue.isEmpty(), "s"); // priority queue is empty
// Save value into the stack to avoid double reading from the storage
uint256 head = _queue.head;
priorityOperation = _queue.data[head];
delete _queue.data[head];
_queue.head = head + 1;
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./PairingsBn254.sol";
library TranscriptLib {
// flip 0xe000000000000000000000000000000000000000000000000000000000000000;
uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint32 constant DST_0 = 0;
uint32 constant DST_1 = 1;
uint32 constant DST_CHALLENGE = 2;
struct Transcript {
bytes32 state_0;
bytes32 state_1;
uint32 challenge_counter;
}
function new_transcript() internal pure returns (Transcript memory t) {
t.state_0 = bytes32(0);
t.state_1 = bytes32(0);
t.challenge_counter = 0;
}
function update_with_u256(Transcript memory self, uint256 value) internal pure {
bytes32 old_state_0 = self.state_0;
self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
}
function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
update_with_u256(self, value.value);
}
function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
update_with_u256(self, p.X);
update_with_u256(self, p.Y);
}
function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
self.challenge_counter += 1;
challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./libraries/PairingsBn254.sol";
import "./libraries/TranscriptLib.sol";
import "../common/libraries/UncheckedMath.sol";
uint256 constant STATE_WIDTH = 4;
uint256 constant NUM_G2_ELS = 2;
struct VerificationKey {
uint256 domain_size;
uint256 num_inputs;
PairingsBn254.Fr omega;
PairingsBn254.G1Point[2] gate_selectors_commitments;
PairingsBn254.G1Point[8] gate_setup_commitments;
PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
PairingsBn254.G1Point lookup_selector_commitment;
PairingsBn254.G1Point[4] lookup_tables_commitments;
PairingsBn254.G1Point lookup_table_type_commitment;
PairingsBn254.Fr[STATE_WIDTH - 1] non_residues;
PairingsBn254.G2Point[NUM_G2_ELS] g2_elements;
}
contract Plonk4VerifierWithAccessToDNext {
using PairingsBn254 for PairingsBn254.G1Point;
using PairingsBn254 for PairingsBn254.G2Point;
using PairingsBn254 for PairingsBn254.Fr;
using TranscriptLib for TranscriptLib.Transcript;
using UncheckedMath for uint256;
struct Proof {
uint256[] input_values;
// commitments
PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments;
PairingsBn254.G1Point copy_permutation_grand_product_commitment;
PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments;
// openings
PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z;
PairingsBn254.Fr[1] state_polys_openings_at_z_omega;
PairingsBn254.Fr[1] gate_selectors_openings_at_z;
PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z;
PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega;
PairingsBn254.Fr quotient_poly_opening_at_z;
PairingsBn254.Fr linearization_poly_opening_at_z;
// lookup commitments
PairingsBn254.G1Point lookup_s_poly_commitment;
PairingsBn254.G1Point lookup_grand_product_commitment;
// lookup openings
PairingsBn254.Fr lookup_s_poly_opening_at_z_omega;
PairingsBn254.Fr lookup_grand_product_opening_at_z_omega;
PairingsBn254.Fr lookup_t_poly_opening_at_z;
PairingsBn254.Fr lookup_t_poly_opening_at_z_omega;
PairingsBn254.Fr lookup_selector_poly_opening_at_z;
PairingsBn254.Fr lookup_table_type_poly_opening_at_z;
PairingsBn254.G1Point opening_proof_at_z;
PairingsBn254.G1Point opening_proof_at_z_omega;
}
struct PartialVerifierState {
PairingsBn254.Fr zero;
PairingsBn254.Fr alpha;
PairingsBn254.Fr beta;
PairingsBn254.Fr gamma;
PairingsBn254.Fr[9] alpha_values;
PairingsBn254.Fr eta;
PairingsBn254.Fr beta_lookup;
PairingsBn254.Fr gamma_lookup;
PairingsBn254.Fr beta_plus_one;
PairingsBn254.Fr beta_gamma;
PairingsBn254.Fr v;
PairingsBn254.Fr u;
PairingsBn254.Fr z;
PairingsBn254.Fr z_omega;
PairingsBn254.Fr z_minus_last_omega;
PairingsBn254.Fr l_0_at_z;
PairingsBn254.Fr l_n_minus_one_at_z;
PairingsBn254.Fr t;
PairingsBn254.G1Point tp;
}
function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at)
internal
view
returns (PairingsBn254.Fr memory num)
{
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size);
PairingsBn254.Fr memory den = at.copy();
den.sub_assign(one);
den.mul_assign(size_fe);
den = den.inverse();
num = at.pow(domain_size);
num.sub_assign(one);
num.mul_assign(den);
}
function evaluate_lagrange_poly_out_of_domain(
uint256 poly_num,
uint256 domain_size,
PairingsBn254.Fr memory omega,
PairingsBn254.Fr memory at
) internal view returns (PairingsBn254.Fr memory res) {
// (omega^i / N) / (X - omega^i) * (X^N - 1)
require(poly_num < domain_size);
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
res = at.pow(domain_size);
res.sub_assign(one);
require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
res.mul_assign(omega_power);
PairingsBn254.Fr memory den = PairingsBn254.copy(at);
den.sub_assign(omega_power);
den.mul_assign(PairingsBn254.new_fr(domain_size));
den = den.inverse();
res.mul_assign(den);
}
function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
internal
view
returns (PairingsBn254.Fr memory res)
{
res = at.pow(domain_size);
res.sub_assign(PairingsBn254.new_fr(1));
}
function initialize_transcript(Proof memory proof, VerificationKey memory vk)
internal
pure
returns (PartialVerifierState memory state)
{
TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript();
for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
transcript.update_with_u256(proof.input_values[i]);
}
for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
transcript.update_with_g1(proof.state_polys_commitments[i]);
}
state.eta = transcript.get_challenge();
transcript.update_with_g1(proof.lookup_s_poly_commitment);
state.beta = transcript.get_challenge();
state.gamma = transcript.get_challenge();
transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
state.beta_lookup = transcript.get_challenge();
state.gamma_lookup = transcript.get_challenge();
transcript.update_with_g1(proof.lookup_grand_product_commitment);
state.alpha = transcript.get_challenge();
for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]);
}
state.z = transcript.get_challenge();
transcript.update_with_fr(proof.quotient_poly_opening_at_z);
for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
transcript.update_with_fr(proof.state_polys_openings_at_z[i]);
}
for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]);
}
for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]);
}
for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]);
}
state.z_omega = state.z.copy();
state.z_omega.mul_assign(vk.omega);
transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega);
transcript.update_with_fr(proof.lookup_t_poly_opening_at_z);
transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z);
transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z);
transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega);
transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega);
transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega);
transcript.update_with_fr(proof.linearization_poly_opening_at_z);
state.v = transcript.get_challenge();
transcript.update_with_g1(proof.opening_proof_at_z);
transcript.update_with_g1(proof.opening_proof_at_z_omega);
state.u = transcript.get_challenge();
}
// compute some powers of challenge alpha([alpha^1, .. alpha^8])
function compute_powers_of_alpha(PartialVerifierState memory state) public pure {
require(state.alpha.value != 0);
state.alpha_values[0] = PairingsBn254.new_fr(1);
state.alpha_values[1] = state.alpha.copy();
PairingsBn254.Fr memory current_alpha = state.alpha.copy();
for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) {
current_alpha.mul_assign(state.alpha);
state.alpha_values[i] = current_alpha.copy();
}
}
function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
// we initialize all challenges beforehand, we can draw each challenge in its own place
PartialVerifierState memory state = initialize_transcript(proof, vk);
if (verify_quotient_evaluation(vk, proof, state) == false) {
return false;
}
require(proof.state_polys_openings_at_z_omega.length == 1);
PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1();
{
// block scope
PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
PairingsBn254.Fr memory current_z = z_in_domain_size.copy();
PairingsBn254.G1Point memory tp;
// start from i =1
for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
tp = proof.quotient_poly_parts_commitments[i].copy_g1();
tp.point_mul_assign(current_z);
quotient_result.point_add_assign(tp);
current_z.mul_assign(z_in_domain_size);
}
}
Queries memory queries = prepare_queries(vk, proof, state);
queries.commitments_at_z[0] = quotient_result;
queries.values_at_z[0] = proof.quotient_poly_opening_at_z;
queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state);
queries.values_at_z[1] = proof.linearization_poly_opening_at_z;
require(queries.commitments_at_z.length == queries.values_at_z.length);
PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0];
PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0];
PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
PairingsBn254.G1Point memory scaled;
for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) {
aggregation_challenge.mul_assign(state.v);
scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge);
aggregated_commitment_at_z.point_add_assign(scaled);
state.t = queries.values_at_z[i];
state.t.mul_assign(aggregation_challenge);
aggregated_opening_at_z.add_assign(state.t);
}
aggregation_challenge.mul_assign(state.v);
PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul(
aggregation_challenge
);
PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0];
aggregated_opening_at_z_omega.mul_assign(aggregation_challenge);
for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) {
aggregation_challenge.mul_assign(state.v);
scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge);
aggregated_commitment_at_z_omega.point_add_assign(scaled);
state.t = queries.values_at_z_omega[i];
state.t.mul_assign(aggregation_challenge);
aggregated_opening_at_z_omega.add_assign(state.t);
}
return
final_pairing(
vk.g2_elements,
proof,
state,
aggregated_commitment_at_z,
aggregated_commitment_at_z_omega,
aggregated_opening_at_z,
aggregated_opening_at_z_omega
);
}
function verify_quotient_evaluation(
VerificationKey memory vk,
Proof memory proof,
PartialVerifierState memory state
) internal view returns (bool) {
uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) {
lagrange_poly_numbers[i] = i;
}
require(vk.num_inputs > 0);
PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z);
state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
inputs_term.add_assign(state.t);
}
inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]);
PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy();
result.add_assign(inputs_term);
// compute powers of alpha
compute_powers_of_alpha(state);
PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
// - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega)
require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1);
PairingsBn254.Fr memory t; // TMP;
for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
t = proof.copy_permutation_polys_openings_at_z[i].copy();
t.mul_assign(state.beta);
t.add_assign(proof.state_polys_openings_at_z[i]);
t.add_assign(state.gamma);
factor.mul_assign(t);
}
t = proof.state_polys_openings_at_z[3].copy();
t.add_assign(state.gamma);
factor.mul_assign(t);
result.sub_assign(factor);
// - L_0(z) * alpha_1
PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z);
l_0_at_z.mul_assign(state.alpha_values[4 + 1]);
result.sub_assign(l_0_at_z);
PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state);
result.add_assign(lookup_quotient_contrib);
PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy();
lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z));
return lhs.value == result.value;
}
function lookup_quotient_contribution(
VerificationKey memory vk,
Proof memory proof,
PartialVerifierState memory state
) internal view returns (PairingsBn254.Fr memory result) {
PairingsBn254.Fr memory t;
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
state.beta_plus_one = state.beta_lookup.copy();
state.beta_plus_one.add_assign(one);
state.beta_gamma = state.beta_plus_one.copy();
state.beta_gamma.mul_assign(state.gamma_lookup);
// (s'*beta + gamma)*(zw')*alpha
t = proof.lookup_s_poly_opening_at_z_omega.copy();
t.mul_assign(state.beta_lookup);
t.add_assign(state.beta_gamma);
t.mul_assign(proof.lookup_grand_product_opening_at_z_omega);
t.mul_assign(state.alpha_values[6]);
// (z - omega^{n-1}) for this part
PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1);
state.z_minus_last_omega = state.z.copy();
state.z_minus_last_omega.sub_assign(last_omega);
t.mul_assign(state.z_minus_last_omega);
result.add_assign(t);
// - alpha_1 * L_{0}(z)
state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
t = state.l_0_at_z.copy();
t.mul_assign(state.alpha_values[6 + 1]);
result.sub_assign(t);
// - alpha_2 * beta_gamma_powered L_{n-1}(z)
PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1);
state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain(
vk.domain_size - 1,
vk.domain_size,
vk.omega,
state.z
);
t = state.l_n_minus_one_at_z.copy();
t.mul_assign(beta_gamma_powered);
t.mul_assign(state.alpha_values[6 + 2]);
result.sub_assign(t);
}
function aggregated_linearization_commitment(
VerificationKey memory vk,
Proof memory proof,
PartialVerifierState memory state
) internal view returns (PairingsBn254.G1Point memory result) {
// qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next)
result = PairingsBn254.new_g1(0, 0);
// Q_a * A
PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul(
proof.state_polys_openings_at_z[0]
);
result.point_add_assign(scaled);
// Q_b * B
scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]);
result.point_add_assign(scaled);
// Q_c * C
scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]);
result.point_add_assign(scaled);
// Q_d * D
scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]);
result.point_add_assign(scaled);
// Q_m* A*B or Q_ab*A*B
PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy();
t.mul_assign(proof.state_polys_openings_at_z[1]);
scaled = vk.gate_setup_commitments[4].point_mul(t);
result.point_add_assign(scaled);
// Q_AC* A*C
t = proof.state_polys_openings_at_z[0].copy();
t.mul_assign(proof.state_polys_openings_at_z[2]);
scaled = vk.gate_setup_commitments[5].point_mul(t);
result.point_add_assign(scaled);
// Q_const
result.point_add_assign(vk.gate_setup_commitments[6]);
// Q_dNext * D_next
scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]);
result.point_add_assign(scaled);
result.point_mul_assign(proof.gate_selectors_openings_at_z[0]);
PairingsBn254.G1Point
memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution(
vk,
proof,
state
);
result.point_add_assign(rescue_custom_gate_linearization_contrib);
require(vk.non_residues.length == STATE_WIDTH - 1);
PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) {
t = state.z.copy();
if (i == 0) {
t.mul_assign(one);
} else {
t.mul_assign(vk.non_residues[i - 1]);
}
t.mul_assign(state.beta);
t.add_assign(state.gamma);
t.add_assign(proof.state_polys_openings_at_z[i]);
factor.mul_assign(t);
unchecked {
++i;
}
}
scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
result.point_add_assign(scaled);
// - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
factor = state.alpha_values[4].copy();
factor.mul_assign(state.beta);
factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
t = proof.copy_permutation_polys_openings_at_z[i].copy();
t.mul_assign(state.beta);
t.add_assign(state.gamma);
t.add_assign(proof.state_polys_openings_at_z[i]);
factor.mul_assign(t);
}
scaled = vk.permutation_commitments[3].point_mul(factor);
result.point_sub_assign(scaled);
// + L_0(z) * Z(x)
state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
require(state.l_0_at_z.value != 0);
factor = state.l_0_at_z.copy();
factor.mul_assign(state.alpha_values[4 + 1]);
scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
result.point_add_assign(scaled);
PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state);
result.point_add_assign(lookup_linearization_contrib);
}
function rescue_custom_gate_linearization_contribution(
VerificationKey memory vk,
Proof memory proof,
PartialVerifierState memory state
) public view returns (PairingsBn254.G1Point memory result) {
PairingsBn254.Fr memory t;
PairingsBn254.Fr memory intermediate_result;
// a^2 - b = 0
t = proof.state_polys_openings_at_z[0].copy();
t.mul_assign(t);
t.sub_assign(proof.state_polys_openings_at_z[1]);
// t.mul_assign(challenge1);
t.mul_assign(state.alpha_values[1]);
intermediate_result.add_assign(t);
// b^2 - c = 0
t = proof.state_polys_openings_at_z[1].copy();
t.mul_assign(t);
t.sub_assign(proof.state_polys_openings_at_z[2]);
t.mul_assign(state.alpha_values[1 + 1]);
intermediate_result.add_assign(t);
// c*a - d = 0;
t = proof.state_polys_openings_at_z[2].copy();
t.mul_assign(proof.state_polys_openings_at_z[0]);
t.sub_assign(proof.state_polys_openings_at_z[3]);
t.mul_assign(state.alpha_values[1 + 2]);
intermediate_result.add_assign(t);
result = vk.gate_selectors_commitments[1].point_mul(intermediate_result);
}
function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state)
internal
view
returns (PairingsBn254.G1Point memory result)
{
PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0);
PairingsBn254.Fr memory t;
PairingsBn254.Fr memory factor;
// s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega)))
factor = proof.lookup_grand_product_opening_at_z_omega.copy();
factor.mul_assign(state.alpha_values[6]);
factor.mul_assign(state.z_minus_last_omega);
PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor);
result.point_add_assign(scaled);
// Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega))
// + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z)
// accumulate coefficient
factor = proof.lookup_t_poly_opening_at_z_omega.copy();
factor.mul_assign(state.beta_lookup);
factor.add_assign(proof.lookup_t_poly_opening_at_z);
factor.add_assign(state.beta_gamma);
// (\\gamma + f(x))
PairingsBn254.Fr memory f_reconstructed;
PairingsBn254.Fr memory current = PairingsBn254.new_fr(1);
PairingsBn254.Fr memory tmp0;
for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
tmp0 = proof.state_polys_openings_at_z[i].copy();
tmp0.mul_assign(current);
f_reconstructed.add_assign(tmp0);
current.mul_assign(state.eta);
}
// add type of table
t = proof.lookup_table_type_poly_opening_at_z.copy();
t.mul_assign(current);
f_reconstructed.add_assign(t);
f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z);
f_reconstructed.add_assign(state.gamma_lookup);
// end of (\\gamma + f(x)) part
factor.mul_assign(f_reconstructed);
factor.mul_assign(state.beta_plus_one);
t = zero.copy();
t.sub_assign(factor);
factor = t;
factor.mul_assign(state.alpha_values[6]);
// Multiply by (z - omega^{n-1})
factor.mul_assign(state.z_minus_last_omega);
// L_{0}(z) in front of Z(x)
t = state.l_0_at_z.copy();
t.mul_assign(state.alpha_values[6 + 1]);
factor.add_assign(t);
// L_{n-1}(z) in front of Z(x)
t = state.l_n_minus_one_at_z.copy();
t.mul_assign(state.alpha_values[6 + 2]);
factor.add_assign(t);
scaled = proof.lookup_grand_product_commitment.point_mul(factor);
result.point_add_assign(scaled);
}
struct Queries {
PairingsBn254.G1Point[13] commitments_at_z;
PairingsBn254.Fr[13] values_at_z;
PairingsBn254.G1Point[6] commitments_at_z_omega;
PairingsBn254.Fr[6] values_at_z_omega;
}
function prepare_queries(
VerificationKey memory vk,
Proof memory proof,
PartialVerifierState memory state
) public view returns (Queries memory queries) {
// we set first two items in calee side so start idx from 2
uint256 idx = 2;
for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
queries.commitments_at_z[idx] = proof.state_polys_commitments[i];
queries.values_at_z[idx] = proof.state_polys_openings_at_z[i];
idx = idx.uncheckedInc();
}
require(proof.gate_selectors_openings_at_z.length == 1);
queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0];
queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0];
idx = idx.uncheckedInc();
for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
queries.commitments_at_z[idx] = vk.permutation_commitments[i];
queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i];
idx = idx.uncheckedInc();
}
queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment;
queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1];
queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega;
queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0];
PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0];
PairingsBn254.Fr memory current_eta = state.eta.copy();
for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) {
state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta);
lookup_t_poly_commitment_aggregated.point_add_assign(state.tp);
current_eta.mul_assign(state.eta);
}
queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated;
queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z;
idx = idx.uncheckedInc();
queries.commitments_at_z[idx] = vk.lookup_selector_commitment;
queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z;
idx = idx.uncheckedInc();
queries.commitments_at_z[idx] = vk.lookup_table_type_commitment;
queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z;
queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment;
queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega;
queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment;
queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega;
queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated;
queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega;
}
function final_pairing(
// VerificationKey memory vk,
PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements,
Proof memory proof,
PartialVerifierState memory state,
PairingsBn254.G1Point memory aggregated_commitment_at_z,
PairingsBn254.G1Point memory aggregated_commitment_at_z_omega,
PairingsBn254.Fr memory aggregated_opening_at_z,
PairingsBn254.Fr memory aggregated_opening_at_z_omega
) internal view returns (bool) {
// q(x) = f(x) - f(z) / (x - z)
// q(x) * (x-z) = f(x) - f(z)
// f(x)
PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1();
aggregated_commitment_at_z_omega.point_mul_assign(state.u);
pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega);
// - f(z)*g
PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy();
aggregated_value.mul_assign(state.u);
aggregated_value.add_assign(aggregated_opening_at_z);
PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value);
pair_with_generator.point_sub_assign(tp);
// +z * q(x)
tp = proof.opening_proof_at_z.point_mul(state.z);
PairingsBn254.Fr memory t = state.z_omega.copy();
t.mul_assign(state.u);
PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t);
tp.point_add_assign(t1);
pair_with_generator.point_add_assign(tp);
// rhs
PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u);
pair_with_x.point_add_assign(proof.opening_proof_at_z);
pair_with_x.negate();
// Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]`
// so we handle it in code generation step
PairingsBn254.G2Point memory first_g2 = g2_elements[0];
PairingsBn254.G2Point memory second_g2 = g2_elements[1];
return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2);
}
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./Verifier.sol";
import "../common/interfaces/IAllowList.sol";
import "./libraries/PriorityQueue.sol";
/// @notice Indicates whether an upgrade is initiated and if yes what type
/// @param None Upgrade is NOT initiated
/// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known
/// @param Shadow Shadow upgrade is initiated, upgrade data is hidden
enum UpgradeState {
None,
Transparent,
Shadow
}
/// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy upgrades and diamond cuts
/// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal
/// @param state Indicates whether an upgrade is initiated and if yes what type
/// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis multisig)
/// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade
/// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals
/// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one
struct UpgradeStorage {
bytes32 proposedUpgradeHash;
UpgradeState state;
address securityCouncil;
bool approvedBySecurityCouncil;
uint40 proposedUpgradeTimestamp;
uint40 currentProposalId;
}
/// @dev The log passed from L2
/// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for the future
/// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
/// This field is required formally but does not have any special meaning.
/// @param txNumberInBlock The L2 transaction number in a block, in which the log was sent
/// @param sender The L2 address which sent the log
/// @param key The 32 bytes of information that was sent in the log
/// @param value The 32 bytes of information that was sent in the log
// Both `key` and `value` are arbitrary 32-bytes selected by the log sender
struct L2Log {
uint8 l2ShardId;
bool isService;
uint16 txNumberInBlock;
address sender;
bytes32 key;
bytes32 value;
}
/// @dev An arbitrary length message passed from L2
/// @notice Under the hood it is `L2Log` sent from the special system L2 contract
/// @param txNumberInBlock The L2 transaction number in a block, in which the message was sent
/// @param sender The address of the L2 account from which the message was passed
/// @param data An arbitrary length message
struct L2Message {
uint16 txNumberInBlock;
address sender;
bytes data;
}
/// @notice Part of the configuration parameters of ZKP circuits
struct VerifierParams {
bytes32 recursionNodeLevelVkHash;
bytes32 recursionLeafLevelVkHash;
bytes32 recursionCircuitsSetVksHash;
}
/// @dev storing all storage variables for zkSync facets
/// NOTE: It is used in a proxy, so it is possible to add new variables to the end
/// NOTE: but NOT to modify already existing variables or change their order
/// NOTE: DiamondCutStorage is unused, but it must remain a member of AppStorage to not have storage collision
/// NOTE: instead UpgradeStorage is used that is appended to the end of the AppStorage struct
struct AppStorage {
/// @dev Storage of variables needed for deprecated diamond cut facet
uint256[7] __DEPRECATED_diamondCutStorage;
/// @notice Address which will exercise governance over the network i.e. change validator set, conduct upgrades
address governor;
/// @notice Address that the governor proposed as one that will replace it
address pendingGovernor;
/// @notice List of permitted validators
mapping(address => bool) validators;
/// @dev Verifier contract. Used to verify aggregated proof for blocks
Verifier verifier;
/// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
uint256 totalBlocksExecuted;
/// @notice Total number of proved blocks i.e. blocks[totalBlocksProved] points at the latest proved block
uint256 totalBlocksVerified;
/// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
uint256 totalBlocksCommitted;
/// @dev Stored hashed StoredBlock for block number
mapping(uint256 => bytes32) storedBlockHashes;
/// @dev Stored root hashes of L2 -> L1 logs
mapping(uint256 => bytes32) l2LogsRootHashes;
/// @dev Container that stores transactions requested from L1
PriorityQueue.Queue priorityQueue;
/// @dev The smart contract that manages the list with permission to call contract functions
IAllowList allowList;
/// @notice Part of the configuration parameters of ZKP circuits. Used as an input for the verifier smart contract
VerifierParams verifierParams;
/// @notice Bytecode hash of bootloader program.
/// @dev Used as an input to zkp-circuit.
bytes32 l2BootloaderBytecodeHash;
/// @notice Bytecode hash of default account (bytecode for EOA).
/// @dev Used as an input to zkp-circuit.
bytes32 l2DefaultAccountBytecodeHash;
/// @dev Indicates that the porter may be touched on L2 transactions.
/// @dev Used as an input to zkp-circuit.
bool zkPorterIsAvailable;
/// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions
/// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e.
/// without overhead for proving the block.
uint256 priorityTxMaxGasLimit;
/// @dev Storage of variables needed for upgrade facet
UpgradeStorage upgrades;
/// @dev A mapping L2 block number => message number => flag.
/// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as
/// a flag to indicate that the message was already processed.
/// @dev Used to indicate that eth withdrawal was already processed
mapping(uint256 => mapping(uint256 => bool)) isEthWithdrawalFinalized;
/// @dev The most recent withdrawal time and amount reset
uint256 __DEPRECATED_lastWithdrawalLimitReset;
/// @dev The accumulated withdrawn amount during the withdrawal limit window
uint256 __DEPRECATED_withdrawnAmountInWindow;
/// @dev A mapping user address => the total deposited amount by the user
mapping(address => uint256) totalDepositedAmountPerUser;
}
pragma solidity ^0.8.13;
// SPDX-License-Identifier: MIT
import "./Plonk4VerifierWithAccessToDNext.sol";
import "../common/libraries/UncheckedMath.sol";
contract Verifier is Plonk4VerifierWithAccessToDNext {
using UncheckedMath for uint256;
function get_verification_key() public pure returns (VerificationKey memory vk) {
vk.num_inputs = 1;
vk.domain_size = 67108864;
vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
// coefficients
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x08fa9d6f0dd6ac1cbeb94ae20fe7a23df05cb1095df66fb561190e615a4037ef,
0x196dcc8692fe322d21375920559944c12ba7b1ba8b732344cf4ba2e3aa0fc8b4
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x0074aaf5d97bd57551311a8b3e4aa7840bc55896502020b2f43ad6a98d81a443,
0x2d275a3ad153dc9d89ebb9c9b6a0afd2dde82470554e9738d905c328fbb4c8bc
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x287f1975a9aeaef5d2bb0767b5ef538f76e82f7da01c0cb6db8c6f920818ec4f,
0x2fff6f53594129f794a7731d963d27e72f385c5c6d8e08829e6f66a9d29a12ea
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x038809fa3d4b7320d43e023454194f0a7878baa7e73a295d2d105260f1c34cbc,
0x25418b1105cf45b2a3da6c349bab1d9caaf145eaf24d1e8fb92c11654c000781
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x0561cafd527ac3f0bc550db77d87cd1c63938f7ec051e62ebf84a5bbe07f9840,
0x28f87201b4cbe19f1517a1c29ca6d6cb074502ccfed4c31c8931c6992c3eea43
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x27e0af572bac6e36d31c33808cb44c0ef8ceee5e2850e916fb01f3747db72491,
0x1da20087ba61c59366b21e31e4ac6889d357cf11bf16b94d875f94f41525c427
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x2c2bcafea8f93d07f96874f470985a8d272c09c8ed49373f36497ee80bd8da17,
0x299276cf6dca1a7e3780f6276c5d067403f6e024e83e0cc1ab4c5f7252b7f653
);
vk.gate_setup_commitments[7] = PairingsBn254.new_g1(
0x0ba9d4a53e050da25b8410045b634f1ca065ff74acd35bab1a72bf1f20047ef3,
0x1f1eefc8b0507a08f852f554bd7abcbd506e52de390ca127477a678d212abfe5
);
// gate selectors
vk.gate_selectors_commitments[0] = PairingsBn254.new_g1(
0x1c6b68d9920620012d85a4850dad9bd6d03ae8bbc7a08b827199e85dba1ef2b1,
0x0f6380560d1b585628ed259289cec19d3a7c70c60e66bbfebfcb70c8c312d91e
);
vk.gate_selectors_commitments[1] = PairingsBn254.new_g1(
0x0dfead780e5067181aae631ff734a33fca302773472997daca58ba49dbd20dcc,
0x00f13fa6e356f525d2fd1c533acf2858c0d2b9f0a9b3180f94e1543929c75073
);
// permutation
vk.permutation_commitments[0] = PairingsBn254.new_g1(
0x1df0747c787934650d99c5696f9273088ad07ec3e0825c9d39685a9b9978ebed,
0x2ace2a277becbc69af4e89518eb50960a733d9d71354845ea43d2e65c8e0e4cb
);
vk.permutation_commitments[1] = PairingsBn254.new_g1(
0x06598c8236a5f5045cd7444dc87f3e1f66f99bf01251e13be4dc0ab1f7f1af4b,
0x14ca234fe9b3bb1e5517fc60d6b90f8ad44b0899a2d4f71a64c9640b3142ce8b
);
vk.permutation_commitments[2] = PairingsBn254.new_g1(
0x01889e2c684caefde60471748f4259196ecf4209a735ccdf7b1816f05bafa50a,
0x092d287a080bfe2fd40ad392ff290e462cd0e347b8fd9d05b90af234ce77a11b
);
vk.permutation_commitments[3] = PairingsBn254.new_g1(
0x0dd98eeb5bc12c221da969398b67750a8774dbdd37a78da52367f9fc0e566d5c,
0x06750ceb40c9fb87fc424df9599340938b7552b759914a90cb0e41d3915c945b
);
// lookup table commitments
vk.lookup_selector_commitment = PairingsBn254.new_g1(
0x2f491c662ae53ceb358f57a868dc00b89befa853bd9a449127ea2d46820995bd,
0x231fe6538634ff8b6fa21ca248fb15e7f43d82eb0bfa705490d24ddb3e3cad77
);
vk.lookup_tables_commitments[0] = PairingsBn254.new_g1(
0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c,
0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b
);
vk.lookup_tables_commitments[1] = PairingsBn254.new_g1(
0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9,
0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca
);
vk.lookup_tables_commitments[2] = PairingsBn254.new_g1(
0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44,
0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765
);
vk.lookup_tables_commitments[3] = PairingsBn254.new_g1(
0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e,
0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e
);
vk.lookup_table_type_commitment = PairingsBn254.new_g1(
0x12cd873a6f18a4a590a846d9ebf61565197edf457efd26bc408eb61b72f37b59,
0x19890cbdac892682e7a5910ca6c238c082130e1c71f33d0c9c901153377770d1
);
// non residues
vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005);
vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007);
vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a);
// g2 elements
vk.g2_elements[0] = PairingsBn254.new_g2(
[
0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
],
[
0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
]
);
vk.g2_elements[1] = PairingsBn254.new_g2(
[
0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
],
[
0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
]
);
}
function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
internal
pure
returns (Proof memory proof)
{
require(serialized_proof.length == 44);
proof.input_values = new uint256[](public_inputs.length);
for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) {
proof.input_values[i] = public_inputs[i];
}
uint256 j;
for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
}
proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
}
for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
}
for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
}
for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
}
for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
}
proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
j = j.uncheckedInc();
proof.opening_proof_at_z = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
j = j.uncheckedAdd(2);
proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j.uncheckedInc()]
);
}
function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
public
view
returns (bool)
{
VerificationKey memory vk = get_verification_key();
require(vk.num_inputs == public_inputs.length);
Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
return verify(proof, vk);
}
}