Skip to content

Commit

Permalink
feat(EVM): Enable EVM emulator using service call (#1141)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xVolosnikov authored Dec 16, 2024
1 parent 93b77a0 commit 9c9233e
Show file tree
Hide file tree
Showing 27 changed files with 185 additions and 153 deletions.
3 changes: 3 additions & 0 deletions l1-contracts/contracts/common/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,6 @@ bytes32 constant TWO_BRIDGES_MAGIC_VALUE = bytes32(uint256(keccak256("TWO_BRIDGE

/// @dev https://eips.ethereum.org/EIPS/eip-1352
address constant BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS = address(uint160(type(uint16).max));

/// @dev Used as the `msg.sender` for system service transactions.
address constant SERVICE_TRANSACTION_SENDER = address(uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF));
12 changes: 12 additions & 0 deletions l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;

/// @notice Defines what types of bytecode are allowed to be deployed on this chain
/// - `EraVm` means that only native contracts can be deployed
/// - `EraVmAndEVM` means that native contracts and EVM contracts can be deployed
enum AllowedBytecodeTypes {
EraVm,
EraVmAndEVM
}

/**
* @author Matter Labs
* @notice System smart contract that is responsible for deploying other smart contracts on a ZKsync hyperchain.
Expand Down Expand Up @@ -29,4 +37,8 @@ interface IL2ContractDeployer {
/// @param _bytecodeHash The correctly formatted hash of the bytecode.
/// @param _input The constructor calldata.
function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external;

/// @notice Changes what types of bytecodes are allowed to be deployed on the chain.
/// @param newAllowedBytecodeTypes The new allowed bytecode types mode.
function setAllowedBytecodeTypesToDeploy(AllowedBytecodeTypes newAllowedBytecodeTypes) external;
}
7 changes: 7 additions & 0 deletions l1-contracts/contracts/governance/ChainAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ contract ChainAdmin is IChainAdmin, Ownable2Step {
emit UpdateUpgradeTimestamp(_protocolVersion, _upgradeTimestamp);
}

/// @notice Enable EVM emulation on chain.
/// @param _chainContract The chain contract address where the EVM emulator will be enabled.
function enableEvmEmulator(IAdmin _chainContract) external onlyOwner returns (bytes32 canonicalTxHash) {
canonicalTxHash = _chainContract.allowEvmEmulation();
emit EnableEvmEmulator();
}

/// @notice Execute multiple calls as part of contract administration.
/// @param _calls Array of Call structures defining target, value, and data for each call.
/// @param _requireSuccess If true, reverts transaction on any call failure.
Expand Down
5 changes: 5 additions & 0 deletions l1-contracts/contracts/governance/IChainAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ interface IChainAdmin {
/// @notice Emitted when the new token multiplier address is set.
event NewTokenMultiplierSetter(address _oldTokenMultiplierSetter, address _newTokenMultiplierSetter);

/// @notice The EVM emulator has been enabled
event EnableEvmEmulator();

function setTokenMultiplierSetter(address _tokenMultiplierSetter) external;

function setUpgradeTimestamp(uint256 _protocolVersion, uint256 _upgradeTimestamp) external;

function multicall(Call[] calldata _calls, bool _requireSuccess) external payable;

function setTokenMultiplier(IAdmin _chainContract, uint128 _nominator, uint128 _denominator) external;

function enableEvmEmulator(IAdmin _chainContract) external returns (bytes32 canonicalTxHash);
}
34 changes: 10 additions & 24 deletions l1-contracts/contracts/state-transition/StateTransitionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {IDiamondInit} from "./chain-interfaces/IDiamondInit.sol";
import {IExecutor} from "./chain-interfaces/IExecutor.sol";
import {IStateTransitionManager, StateTransitionManagerInitializeData, ChainCreationParams} from "./IStateTransitionManager.sol";
import {ISystemContext} from "./l2-deps/ISystemContext.sol";
import {AllowedBytecodeTypes} from "./l2-deps/AllowedBytecodeTypes.sol";
import {IZkSyncHyperchain} from "./chain-interfaces/IZkSyncHyperchain.sol";
import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol";
import {L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_FORCE_DEPLOYER_ADDR} from "../common/L2ContractAddresses.sol";
Expand Down Expand Up @@ -323,15 +322,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
/// registration

/// @dev we have to set the chainId at genesis, as blockhashzero is the same for all chains with the same chainId
function _setChainConfigurationUpgrade(
uint256 _chainId,
AllowedBytecodeTypes _allowedBytecodeTypesMode,
address _chainContract
) internal {
bytes memory systemContextCalldata = abi.encodeCall(
ISystemContext.setChainConfiguration,
(_chainId, uint256(_allowedBytecodeTypesMode))
);
function _setChainIdUpgrade(uint256 _chainId, address _chainContract) internal {
bytes memory systemContextCalldata = abi.encodeCall(ISystemContext.setChainId, (_chainId));
uint256[] memory uintEmptyArray;
bytes[] memory bytesEmptyArray;

Expand Down Expand Up @@ -404,32 +396,26 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
/// @param _baseToken the base token address used to pay for gas fees
/// @param _sharedBridge the shared bridge address, used as base token bridge
/// @param _admin the chain's admin address
/// @param _inputData the input data for chain creation
/// @param _diamondCut the diamond cut data that initializes the chains Diamond Proxy
function createNewChain(
uint256 _chainId,
address _baseToken,
address _sharedBridge,
address _admin,
bytes calldata _inputData
bytes calldata _diamondCut
) external onlyBridgehub {
if (getHyperchain(_chainId) != address(0)) {
// Hyperchain already registered
return;
}

// check not registered
(bytes memory _diamondCut, AllowedBytecodeTypes allowedBytecodeTypesMode) = abi.decode(
_inputData,
(bytes, AllowedBytecodeTypes)
);
Diamond.DiamondCutData memory diamondCut = abi.decode(_diamondCut, (Diamond.DiamondCutData));

{
// check input
bytes32 cutHashInput = keccak256(_diamondCut);
if (cutHashInput != initialCutHash) {
revert HashMismatch(initialCutHash, cutHashInput);
}
// check input
bytes32 cutHashInput = keccak256(_diamondCut);
if (cutHashInput != initialCutHash) {
revert HashMismatch(initialCutHash, cutHashInput);
}

// construct init data
Expand Down Expand Up @@ -459,8 +445,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own

_registerNewHyperchain(_chainId, hyperchainAddress);

// set chain configuration: chainId in VM and allowed bytecode types
_setChainConfigurationUpgrade(_chainId, allowedBytecodeTypesMode, hyperchainAddress);
// set chainId in VM
_setChainIdUpgrade(_chainId, hyperchainAddress);
}

/// @dev This internal function is used to register a new hyperchain in the system.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
pragma solidity 0.8.24;

import {IAdmin} from "../../chain-interfaces/IAdmin.sol";
import {IMailbox} from "../../chain-interfaces/IMailbox.sol";
import {Diamond} from "../../libraries/Diamond.sol";
import {MAX_GAS_PER_TRANSACTION} from "../../../common/Config.sol";
import {FeeParams, PubdataPricingMode} from "../ZkSyncHyperchainStorage.sol";
import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol";
import {IStateTransitionManager} from "../../IStateTransitionManager.sol";
import {Unauthorized, TooMuchGas, PriorityTxPubdataExceedsMaxPubDataPerBatch, InvalidPubdataPricingMode, ProtocolIdMismatch, ChainAlreadyLive, HashMismatch, ProtocolIdNotGreater, DenominatorIsZero, DiamondAlreadyFrozen, DiamondNotFrozen} from "../../../common/L1ContractErrors.sol";
import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "../../../common/L2ContractAddresses.sol";
import {IL2ContractDeployer, AllowedBytecodeTypes} from "../../../common/interfaces/IL2ContractDeployer.sol";

// While formally the following import is not used, it is needed to inherit documentation from it
import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol";
Expand Down Expand Up @@ -119,6 +122,15 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin {
emit NewTransactionFilterer(oldTransactionFilterer, _transactionFilterer);
}

/// @inheritdoc IAdmin
function allowEvmEmulation() external onlyAdmin returns (bytes32 canonicalTxHash) {
canonicalTxHash = IMailbox(address(this)).requestL2ServiceTransaction(
L2_DEPLOYER_SYSTEM_CONTRACT_ADDR,
abi.encodeCall(IL2ContractDeployer.setAllowedBytecodeTypesToDeploy, AllowedBytecodeTypes.EraVmAndEVM)
);
emit EnableEvmEmulator();
}

/*//////////////////////////////////////////////////////////////
UPGRADE EXECUTION
//////////////////////////////////////////////////////////////*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol";
import {L2ContractHelper} from "../../../common/libraries/L2ContractHelper.sol";
import {AddressAliasHelper} from "../../../vendor/AddressAliasHelper.sol";
import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol";
import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, ETH_TOKEN_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, PRIORITY_OPERATION_L2_TX_TYPE, PRIORITY_EXPIRATION, MAX_NEW_FACTORY_DEPS} from "../../../common/Config.sol";
import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, ETH_TOKEN_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, PRIORITY_OPERATION_L2_TX_TYPE, PRIORITY_EXPIRATION, MAX_NEW_FACTORY_DEPS, SERVICE_TRANSACTION_SENDER} from "../../../common/Config.sol";
import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR} from "../../../common/L2ContractAddresses.sol";

import {IL1SharedBridge} from "../../../bridge/interfaces/IL1SharedBridge.sol";
Expand Down Expand Up @@ -248,6 +248,28 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox {
);
}

/// @inheritdoc IMailbox
function requestL2ServiceTransaction(
address _contractL2,
bytes calldata _l2Calldata
) external onlySelf returns (bytes32 canonicalTxHash) {
canonicalTxHash = _requestL2TransactionFree(
BridgehubL2TransactionRequest({
sender: SERVICE_TRANSACTION_SENDER,
contractL2: _contractL2,
mintValue: 0,
l2Value: 0,
// Very large amount
l2GasLimit: 72_000_000,
l2Calldata: _l2Calldata,
l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA,
factoryDeps: new bytes[](0),
// Tx is free, so no refund recipient needed
refundRecipient: address(0)
})
);
}

function _requestL2TransactionSender(
BridgehubL2TransactionRequest memory _request
) internal nonReentrant returns (bytes32 canonicalTxHash) {
Expand Down Expand Up @@ -313,6 +335,19 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox {
canonicalTxHash = _writePriorityOp(_params);
}

function _requestL2TransactionFree(
BridgehubL2TransactionRequest memory _request
) internal nonReentrant returns (bytes32 canonicalTxHash) {
WritePriorityOpParams memory params = WritePriorityOpParams({
request: _request,
txId: s.priorityQueue.getTotalPriorityTxs(),
l2GasPrice: 0,
expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION)
});

canonicalTxHash = _writePriorityOp(params);
}

function _serializeL2Transaction(
WritePriorityOpParams memory _priorityOpParams
) internal pure returns (L2CanonicalTransaction memory transaction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,11 @@ contract ZkSyncHyperchainBase is ReentrancyGuard {
}
_;
}

modifier onlySelf() {
if (msg.sender != address(this)) {
revert Unauthorized(msg.sender);
}
_;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ interface IAdmin is IZkSyncHyperchainBase {
/// @notice Set the transaction filterer
function setTransactionFilterer(address _transactionFilterer) external;

/// @notice Allow EVM emulation on chain
function allowEvmEmulation() external returns (bytes32 canonicalTxHash);

/// @notice Perform the upgrade from the current protocol version with the corresponding upgrade data
/// @param _protocolVersion The current protocol version from which upgrade is executed
/// @param _cutData The diamond cut parameters that is executed in the upgrade
Expand Down Expand Up @@ -105,4 +108,7 @@ interface IAdmin is IZkSyncHyperchainBase {

/// @notice Emitted when the contract is unfrozen.
event Unfreeze();

/// @notice The EVM emulator has been enabled
event EnableEvmEmulator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ interface IMailbox is IZkSyncHyperchainBase {
address _refundRecipient
) external payable returns (bytes32 canonicalTxHash);

/// @notice Request execution of service L2 transaction from L1.
/// @dev Used for chain configuration. Can be called only by DiamondProxy itself.
/// @param _contractL2 The L2 receiver address
/// @param _l2Calldata The input of the L2 transaction
function requestL2ServiceTransaction(
address _contractL2,
bytes calldata _l2Calldata
) external returns (bytes32 canonicalTxHash);

function bridgehubRequestL2Transaction(
BridgehubL2TransactionRequest calldata _request
) external returns (bytes32 canonicalTxHash);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@
pragma solidity ^0.8.21;

interface ISystemContext {
/// @notice Set the chain configuration.
/// @param _newChainId The chainId
/// @param _newAllowedBytecodeTypes The new allowed bytecode types mode.
function setChainConfiguration(uint256 _newChainId, uint256 _newAllowedBytecodeTypes) external;
function setChainId(uint256 _newChainId) external;
}
18 changes: 18 additions & 0 deletions l1-contracts/deploy-scripts/EnableEvmEmulator.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {Script} from "forge-std/Script.sol";

import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol";
import {IChainAdmin} from "contracts/governance/IChainAdmin.sol";

contract EnableEvmEmulator is Script {
// This function should be called by the owner to update token multiplier setter role
function chainAllowEvmEmulation(address chainAdmin, address target) public {
IChainAdmin admin = IChainAdmin(chainAdmin);

vm.startBroadcast();
admin.enableEvmEmulator(IZkSyncHyperchain(target));
vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"
import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol";
import {L1SharedBridge} from "contracts/bridge/L1SharedBridge.sol";
import {IStateTransitionManager} from "contracts/state-transition/IStateTransitionManager.sol";
import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol";
import {IGovernance} from "contracts/governance/IGovernance.sol";
import {IChainAdmin} from "contracts/governance/IChainAdmin.sol";
import {Call} from "contracts/governance/Common.sol";
import {Utils} from "./Utils.sol";

Expand Down Expand Up @@ -276,12 +274,6 @@ contract PrepareZKChainRegistrationCalldataScript is Script {
function prepareRegisterHyperchainCall() internal view returns (Call memory) {
Bridgehub bridgehub = Bridgehub(ecosystem.bridgehub);

AllowedBytecodeTypes allowedBytecodeTypesMode = config.allowEvmEmulator
? AllowedBytecodeTypes.EraVmAndEVM
: AllowedBytecodeTypes.EraVm;

bytes memory diamondCutEncoded = abi.encode(config.diamondCutData);

bytes memory data = abi.encodeCall(
bridgehub.createNewChain,
(
Expand All @@ -290,7 +282,7 @@ contract PrepareZKChainRegistrationCalldataScript is Script {
config.baseToken,
config.bridgehubCreateNewChainSalt,
config.chainAdmin,
abi.encode(diamondCutEncoded, allowedBytecodeTypesMode)
config.diamondCutData
)
);

Expand Down
10 changes: 1 addition & 9 deletions l1-contracts/deploy-scripts/RegisterHyperchain.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {stdToml} from "forge-std/StdToml.sol";

import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol";
import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol";
import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol";
import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol";
import {Governance} from "contracts/governance/Governance.sol";
import {ChainAdmin} from "contracts/governance/ChainAdmin.sol";
Expand Down Expand Up @@ -157,14 +156,7 @@ contract RegisterHyperchainScript is Script {
function registerHyperchain() internal {
Bridgehub bridgehub = Bridgehub(config.bridgehub);

AllowedBytecodeTypes allowedBytecodeTypesMode = config.allowEvmEmulator
? AllowedBytecodeTypes.EraVmAndEVM
: AllowedBytecodeTypes.EraVm;

bytes memory initData = abi.encode(config.diamondCutData, allowedBytecodeTypesMode);

vm.recordLogs();

bytes memory data = abi.encodeCall(
bridgehub.createNewChain,
(
Expand All @@ -173,7 +165,7 @@ contract RegisterHyperchainScript is Script {
config.baseToken,
config.bridgehubCreateNewChainSalt,
msg.sender,
initData
config.diamondCutData
)
);

Expand Down
Loading

0 comments on commit 9c9233e

Please sign in to comment.