Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(EVM): Enable EVM emulator using service call #1141

Merged
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));
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);
}
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
Loading