diff --git a/l1-contracts/contracts/dev-contracts/test/CustomUpgradeTest.sol b/l1-contracts/contracts/dev-contracts/test/CustomUpgradeTest.sol index 7055ce557..c1c3d2f4c 100644 --- a/l1-contracts/contracts/dev-contracts/test/CustomUpgradeTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/CustomUpgradeTest.sol @@ -31,7 +31,12 @@ contract CustomUpgradeTest is BaseZkSyncUpgrade { (uint32 newMinorVersion, bool isPatchOnly) = _setNewProtocolVersion(_proposedUpgrade.newProtocolVersion); _upgradeL1Contract(_proposedUpgrade.l1ContractsUpgradeCalldata); _upgradeVerifier(_proposedUpgrade.verifier, _proposedUpgrade.verifierParams); - _setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash, isPatchOnly); + _setBaseSystemContracts( + _proposedUpgrade.bootloaderHash, + _proposedUpgrade.defaultAccountHash, + _proposedUpgrade.evmSimulatorHash, + isPatchOnly + ); bytes32 txHash; txHash = _setL2SystemContractUpgrade( diff --git a/l1-contracts/contracts/dev-contracts/test/ExecutorProvingTest.sol b/l1-contracts/contracts/dev-contracts/test/ExecutorProvingTest.sol index 50bccb744..1ff8fc2b6 100644 --- a/l1-contracts/contracts/dev-contracts/test/ExecutorProvingTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/ExecutorProvingTest.sol @@ -33,9 +33,14 @@ contract ExecutorProvingTest is ExecutorFacet { } /// Sets the DefaultAccount Hash and Bootloader Hash. - function setHashes(bytes32 l2DefaultAccountBytecodeHash, bytes32 l2BootloaderBytecodeHash) external { + function setHashes( + bytes32 l2DefaultAccountBytecodeHash, + bytes32 l2BootloaderBytecodeHash, + bytes32 l2EvmSimulatorBytecode + ) external { s.l2DefaultAccountBytecodeHash = l2DefaultAccountBytecodeHash; s.l2BootloaderBytecodeHash = l2BootloaderBytecodeHash; + s.l2EvmSimulatorBytecodeHash = l2EvmSimulatorBytecode; s.zkPorterIsAvailable = false; } } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index 0dbdd2ad1..cbb8706d6 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -356,6 +356,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own factoryDeps: bytesEmptyArray, bootloaderHash: bytes32(0), defaultAccountHash: bytes32(0), + evmSimulatorHash: bytes32(0), verifier: address(0), verifierParams: VerifierParams({ recursionNodeLevelVkHash: bytes32(0), diff --git a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol index 663cf260a..531e3698a 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol @@ -62,6 +62,7 @@ contract DiamondInit is ZkSyncHyperchainBase, IDiamondInit { s.__DEPRECATED_verifierParams = _initializeData.verifierParams; s.l2BootloaderBytecodeHash = _initializeData.l2BootloaderBytecodeHash; s.l2DefaultAccountBytecodeHash = _initializeData.l2DefaultAccountBytecodeHash; + s.l2EvmSimulatorBytecodeHash = _initializeData.l2EvmSimulatorBytecodeHash; s.priorityTxMaxGasLimit = _initializeData.priorityTxMaxGasLimit; s.feeParams = _initializeData.feeParams; s.blobVersionedHashRetriever = _initializeData.blobVersionedHashRetriever; diff --git a/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol b/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol index a06921fdb..b490d2f62 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol @@ -98,6 +98,9 @@ struct ZkSyncHyperchainStorage { /// @notice Bytecode hash of default account (bytecode for EOA). /// @dev Used as an input to zkp-circuit. bytes32 l2DefaultAccountBytecodeHash; + /// @notice Bytecode hash of evm simulator. + /// @dev Used as an input to zkp-circuit. + bytes32 l2EvmSimulatorBytecodeHash; /// @dev Indicates that the porter may be touched on L2 transactions. /// @dev Used as an input to zkp-circuit. bool zkPorterIsAvailable; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol index 07995642b..c157a575d 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol @@ -600,13 +600,13 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { function _batchMetaParameters() internal view returns (bytes memory) { bytes32 l2DefaultAccountBytecodeHash = s.l2DefaultAccountBytecodeHash; + bytes32 l2EvmSimulatorBytecodeHash = s.l2EvmSimulatorBytecodeHash; return abi.encodePacked( s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, l2DefaultAccountBytecodeHash, - // VM 1.5.0 requires us to pass the EVM simulator code hash. For now it is the same as the default account. - l2DefaultAccountBytecodeHash + l2EvmSimulatorBytecodeHash ); } diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol index 604eeef2e..5ea7a92fa 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol @@ -142,6 +142,11 @@ contract GettersFacet is ZkSyncHyperchainBase, IGetters, ILegacyGetters { return s.l2DefaultAccountBytecodeHash; } + /// @inheritdoc IGetters + function getL2EvmSimulatorBytecodeHash() external view returns (bytes32) { + return s.l2EvmSimulatorBytecodeHash; + } + /// @inheritdoc IGetters function getVerifierParams() external view returns (VerifierParams memory) { return s.__DEPRECATED_verifierParams; diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol index 189ae69fa..ebc8686c6 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol @@ -36,6 +36,7 @@ struct InitializeData { VerifierParams verifierParams; bytes32 l2BootloaderBytecodeHash; bytes32 l2DefaultAccountBytecodeHash; + bytes32 l2EvmSimulatorBytecodeHash; uint256 priorityTxMaxGasLimit; FeeParams feeParams; address blobVersionedHashRetriever; @@ -53,6 +54,7 @@ struct InitializeDataNewChain { VerifierParams verifierParams; bytes32 l2BootloaderBytecodeHash; bytes32 l2DefaultAccountBytecodeHash; + bytes32 l2EvmSimulatorBytecodeHash; uint256 priorityTxMaxGasLimit; FeeParams feeParams; address blobVersionedHashRetriever; diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol index 5da8cc748..dbcac8b57 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol @@ -81,6 +81,9 @@ interface IGetters is IZkSyncHyperchainBase { /// @return Bytecode hash of default account (bytecode for EOA). function getL2DefaultAccountBytecodeHash() external view returns (bytes32); + /// @return Bytecode hash of EVM simulator. + function getL2EvmSimulatorBytecodeHash() external view returns (bytes32); + /// @return Verifier parameters. /// @dev This function is deprecated and will soon be removed. function getVerifierParams() external view returns (VerifierParams memory); diff --git a/l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol b/l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol index 4534884d5..b4c941989 100644 --- a/l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol +++ b/l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +// solhint-disable reason-string, gas-custom-errors + pragma solidity 0.8.24; import {SafeCast} from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol"; @@ -33,6 +35,7 @@ struct ProposedUpgrade { bytes[] factoryDeps; bytes32 bootloaderHash; bytes32 defaultAccountHash; + bytes32 evmSimulatorHash; address verifier; VerifierParams verifierParams; bytes l1ContractsUpgradeCalldata; @@ -54,6 +57,8 @@ abstract contract BaseZkSyncUpgrade is ZkSyncHyperchainBase { /// @notice Сhanges to the bytecode that is used in L2 as a default account event NewL2DefaultAccountBytecodeHash(bytes32 indexed previousBytecodeHash, bytes32 indexed newBytecodeHash); + event NewL2EvmSimulatorBytecodeHash(bytes32 indexed previousBytecodeHash, bytes32 indexed newBytecodeHash); + /// @notice Verifier address changed event NewVerifier(address indexed oldVerifier, address indexed newVerifier); @@ -79,7 +84,12 @@ abstract contract BaseZkSyncUpgrade is ZkSyncHyperchainBase { (uint32 newMinorVersion, bool isPatchOnly) = _setNewProtocolVersion(_proposedUpgrade.newProtocolVersion); _upgradeL1Contract(_proposedUpgrade.l1ContractsUpgradeCalldata); _upgradeVerifier(_proposedUpgrade.verifier, _proposedUpgrade.verifierParams); - _setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash, isPatchOnly); + _setBaseSystemContracts( + _proposedUpgrade.bootloaderHash, + _proposedUpgrade.defaultAccountHash, + _proposedUpgrade.evmSimulatorHash, + isPatchOnly + ); txHash = _setL2SystemContractUpgrade( _proposedUpgrade.l2ProtocolUpgradeTx, @@ -115,6 +125,26 @@ abstract contract BaseZkSyncUpgrade is ZkSyncHyperchainBase { emit NewL2DefaultAccountBytecodeHash(previousDefaultAccountBytecodeHash, _l2DefaultAccountBytecodeHash); } + /// @notice Change default account bytecode hash, that is used on L2 + /// @param _l2EvmSimulatorBytecodeHash The hash of default account L2 bytecode + /// @param _patchOnly Whether only the patch part of the protocol version semver has changed + function _setL2EvmSimulatorBytecodeHash(bytes32 _l2EvmSimulatorBytecodeHash, bool _patchOnly) private { + if (_l2EvmSimulatorBytecodeHash == bytes32(0)) { + return; + } + + require(!_patchOnly, "Patch only upgrade can not set new default account"); + + L2ContractHelper.validateBytecodeHash(_l2EvmSimulatorBytecodeHash); + + // Save previous value into the stack to put it into the event later + bytes32 previousL2EvmSimulatorBytecodeHash = s.l2EvmSimulatorBytecodeHash; + + // Change the default account bytecode hash + s.l2EvmSimulatorBytecodeHash = _l2EvmSimulatorBytecodeHash; + emit NewL2EvmSimulatorBytecodeHash(previousL2EvmSimulatorBytecodeHash, _l2EvmSimulatorBytecodeHash); + } + /// @notice Change bootloader bytecode hash, that is used on L2 /// @param _l2BootloaderBytecodeHash The hash of bootloader L2 bytecode /// @param _patchOnly Whether only the patch part of the protocol version semver has changed @@ -185,9 +215,15 @@ abstract contract BaseZkSyncUpgrade is ZkSyncHyperchainBase { /// @param _bootloaderHash The hash of the new bootloader bytecode. If zero, it will not be updated. /// @param _defaultAccountHash The hash of the new default account bytecode. If zero, it will not be updated. /// @param _patchOnly Whether only the patch part of the protocol version semver has changed. - function _setBaseSystemContracts(bytes32 _bootloaderHash, bytes32 _defaultAccountHash, bool _patchOnly) internal { + function _setBaseSystemContracts( + bytes32 _bootloaderHash, + bytes32 _defaultAccountHash, + bytes32 _evmSimulatorHash, + bool _patchOnly + ) internal { _setL2BootloaderBytecodeHash(_bootloaderHash, _patchOnly); _setL2DefaultAccountBytecodeHash(_defaultAccountHash, _patchOnly); + _setL2EvmSimulatorBytecodeHash(_evmSimulatorHash, _patchOnly); } /// @notice Sets the hash of the L2 system contract upgrade transaction for the next batch to be committed diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index cef851957..167abf40d 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -120,6 +120,7 @@ contract DeployL1Script is Script { bytes diamondCutData; bytes32 bootloaderHash; bytes32 defaultAAHash; + bytes32 evmSimulatorHash; } struct TokensConfig { @@ -442,6 +443,7 @@ contract DeployL1Script is Script { verifierParams: verifierParams, l2BootloaderBytecodeHash: config.contracts.bootloaderHash, l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, + l2EvmSimulatorBytecodeHash: config.contracts.evmSimulatorHash, priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, feeParams: feeParams, blobVersionedHashRetriever: addresses.blobVersionedHashRetriever diff --git a/l1-contracts/scripts/token-migration.ts b/l1-contracts/scripts/token-migration.ts index b18260ca3..1ea0d1a12 100644 --- a/l1-contracts/scripts/token-migration.ts +++ b/l1-contracts/scripts/token-migration.ts @@ -233,11 +233,15 @@ export async function transferTokensOnForkedNetwork(deployer: Deployer) { const erc20contract = IERC20Factory.connect(tokenAddress, provider); console.log(`Migrating token ${tokenAddress}`); console.log( - `Balance before: ${await erc20contract.balanceOf(deployer.addresses.Bridges.ERC20BridgeProxy)}, ${await erc20contract.balanceOf(deployer.addresses.Bridges.SharedBridgeProxy)}` + `Balance before: ${await erc20contract.balanceOf( + deployer.addresses.Bridges.ERC20BridgeProxy + )}, ${await erc20contract.balanceOf(deployer.addresses.Bridges.SharedBridgeProxy)}` ); await transferTokens(deployer, tokenAddress); console.log( - `Balance after: ${await erc20contract.balanceOf(deployer.addresses.Bridges.ERC20BridgeProxy)}, ${await erc20contract.balanceOf(deployer.addresses.Bridges.SharedBridgeProxy)}` + `Balance after: ${await erc20contract.balanceOf( + deployer.addresses.Bridges.ERC20BridgeProxy + )}, ${await erc20contract.balanceOf(deployer.addresses.Bridges.SharedBridgeProxy)}` ); } for (const tokenAddress of tokenList) { diff --git a/l1-contracts/scripts/upgrade-consistency-checker.ts b/l1-contracts/scripts/upgrade-consistency-checker.ts index 530d47dc3..49f444da4 100644 --- a/l1-contracts/scripts/upgrade-consistency-checker.ts +++ b/l1-contracts/scripts/upgrade-consistency-checker.ts @@ -66,8 +66,9 @@ const expectedGenesisRoot = "0xabdb766b18a479a5c783a4b80e12686bc8ea3cc2d8a305049 const expectedRecursionNodeLevelVkHash = "0xf520cd5b37e74e19fdb369c8d676a04dce8a19457497ac6686d2bb95d94109c8"; const expectedRecursionLeafLevelVkHash = "0xf9664f4324c1400fa5c3822d667f30e873f53f1b8033180cd15fe41c1e2355c6"; const expectedRecursionCircuitsSetVksHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; -const expectedBootloaderHash = "0x010008e742608b21bf7eb23c1a9d0602047e3618b464c9b59c0fba3b3d7ab66e"; -const expectedDefaultAccountHash = "0x01000563374c277a2c1e34659a2a1e87371bb6d852ce142022d497bfb50b9e32"; +const expectedBootloaderHash = "0x010008e7894d0dd14681c76bdb4d5e4e7f6b51bfe40c957d50eed3fec829fdb0"; +const expectedDefaultAccountHash = "0x0100058deb36e1f2eeb48bf3846d0e8eb38e9176754b73116bb41a472459a4dd"; +const expectedEvmSimulatorHash = "0x01000f197081a9906cc411d0698c4961aeb5c74877f37f7071681da6e8ef3f31"; const validatorOne = process.env.ETH_SENDER_SENDER_OPERATOR_COMMIT_ETH_ADDR!; const validatorTwo = process.env.ETH_SENDER_SENDER_OPERATOR_BLOBS_ETH_ADDR!; @@ -221,6 +222,7 @@ async function extractProxyInitializationData(contract: ethers.Contract, data: s recursionCircuitsSetVksHash, l2BootloaderBytecodeHash, l2DefaultAccountBytecodeHash, + l2EvmSimulatorBytecodeHash, // priorityTxMaxGasLimit, // // We unpack fee params @@ -240,6 +242,7 @@ async function extractProxyInitializationData(contract: ethers.Contract, data: s "bytes32", "bytes32", "bytes32", + "bytes32", "uint256", "uint256", "uint256", @@ -276,6 +279,10 @@ async function extractProxyInitializationData(contract: ethers.Contract, data: s throw new Error("L2 default account bytecode hash is not correct"); } + if (l2EvmSimulatorBytecodeHash.toLowerCase() !== expectedEvmSimulatorHash.toLowerCase()) { + throw new Error("L2 default account bytecode hash is not correct"); + } + console.log("STM init data correct!"); } diff --git a/l1-contracts/scripts/utils.ts b/l1-contracts/scripts/utils.ts index 5ae1bceac..e60434a8f 100644 --- a/l1-contracts/scripts/utils.ts +++ b/l1-contracts/scripts/utils.ts @@ -70,6 +70,11 @@ export function readSystemContractsBytecode(fileName: string) { return JSON.parse(artifact.toString()).bytecode; } +export function readEvmSimulatorbytecode() { + const systemContractsPath = path.join(process.env.ZKSYNC_HOME as string, "contracts/system-contracts"); + return fs.readFileSync(`${systemContractsPath}/contracts-preprocessed/artifacts/EvmInterpreter.yul.zbin`); +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function print(name: string, data: any) { console.log(`${name}:\n`, JSON.stringify(data, null, 4), "\n"); diff --git a/l1-contracts/src.ts/deploy-process.ts b/l1-contracts/src.ts/deploy-process.ts index 285dd9e0e..42ab1329f 100644 --- a/l1-contracts/src.ts/deploy-process.ts +++ b/l1-contracts/src.ts/deploy-process.ts @@ -16,6 +16,7 @@ import { ADDRESS_ONE } from "../src.ts/utils"; export const L2_BOOTLOADER_BYTECODE_HASH = "0x1000100000000000000000000000000000000000000000000000000000000000"; export const L2_DEFAULT_ACCOUNT_BYTECODE_HASH = "0x1001000000000000000000000000000000000000000000000000000000000000"; +export const L2_EVM_SIMULATOR_BYTECODE_HASH = "0x1010000000000000000000000000000000000000000000000000000000000000"; export async function initialBridgehubDeployment( deployer: Deployer, diff --git a/l1-contracts/src.ts/deploy-test-process.ts b/l1-contracts/src.ts/deploy-test-process.ts index b8af27b34..09965b3d8 100644 --- a/l1-contracts/src.ts/deploy-test-process.ts +++ b/l1-contracts/src.ts/deploy-test-process.ts @@ -15,6 +15,7 @@ import { Deployer } from "./deploy"; import { L2_BOOTLOADER_BYTECODE_HASH, L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + L2_EVM_SIMULATOR_BYTECODE_HASH, initialBridgehubDeployment, registerHyperchain, } from "./deploy-process"; @@ -65,6 +66,7 @@ export async function defaultDeployerForTests(deployWallet: Wallet, ownerAddress addresses: addressConfig, bootloaderBytecodeHash: L2_BOOTLOADER_BYTECODE_HASH, defaultAccountBytecodeHash: L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + evmSimulatorBytecodeHash: L2_EVM_SIMULATOR_BYTECODE_HASH, }); } @@ -76,6 +78,7 @@ export async function defaultEraDeployerForTests(deployWallet: Wallet, ownerAddr addresses: addressConfig, bootloaderBytecodeHash: L2_BOOTLOADER_BYTECODE_HASH, defaultAccountBytecodeHash: L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + evmSimulatorBytecodeHash: L2_EVM_SIMULATOR_BYTECODE_HASH, }); const l2_rpc_addr = "http://localhost:3050"; const web3Provider = new zkethers.Provider(l2_rpc_addr); @@ -315,6 +318,7 @@ export class EraDeployer extends Deployer { verifierParams, l2BootloaderBytecodeHash: L2_BOOTLOADER_BYTECODE_HASH, l2DefaultAccountBytecodeHash: L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + l2EvmSimulatorBytecodeHash: L2_EVM_SIMULATOR_BYTECODE_HASH, priorityTxMaxGasLimit, feeParams, blobVersionedHashRetriever: this.addresses.BlobVersionedHashRetriever, diff --git a/l1-contracts/src.ts/deploy-utils.ts b/l1-contracts/src.ts/deploy-utils.ts index 71b4d0c31..c431acc5e 100644 --- a/l1-contracts/src.ts/deploy-utils.ts +++ b/l1-contracts/src.ts/deploy-utils.ts @@ -72,7 +72,9 @@ export async function deployBytecodeViaCreate2( const gasUsed = receipt.gasUsed; log( - `${contractName} deployed, gasUsed: ${gasUsed.toString()}, tx hash: ${tx.hash}, expected address: ${expectedAddress}` + `${contractName} deployed, gasUsed: ${gasUsed.toString()}, tx hash: ${ + tx.hash + }, expected address: ${expectedAddress}` ); const deployedBytecodeAfter = await deployWallet.provider.getCode(expectedAddress); diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index d131d269c..889a699a9 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -10,6 +10,7 @@ import { packSemver, readBatchBootloaderBytecode, readSystemContractsBytecode, + readEvmSimulatorbytecode, unpackStringSemVer, } from "../scripts/utils"; import { getTokens } from "./deploy-token"; @@ -43,6 +44,7 @@ import type { Contract, Overrides } from "@ethersproject/contracts"; let L2_BOOTLOADER_BYTECODE_HASH: string; let L2_DEFAULT_ACCOUNT_BYTECODE_HASH: string; +let L2_EVM_SIMULATOR_BYTECODE_HASH: string; export interface DeployerConfig { deployWallet: Wallet; @@ -51,6 +53,7 @@ export interface DeployerConfig { verbose?: boolean; bootloaderBytecodeHash?: string; defaultAccountBytecodeHash?: string; + evmSimulatorBytecodeHash?: string; } export interface Operation { @@ -78,6 +81,9 @@ export class Deployer { L2_DEFAULT_ACCOUNT_BYTECODE_HASH = config.defaultAccountBytecodeHash ? config.defaultAccountBytecodeHash : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); + L2_EVM_SIMULATOR_BYTECODE_HASH = config.evmSimulatorBytecodeHash + ? config.evmSimulatorBytecodeHash + : hexlify(hashL2Bytecode(readEvmSimulatorbytecode())); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); } @@ -105,6 +111,7 @@ export class Deployer { verifierParams, L2_BOOTLOADER_BYTECODE_HASH, L2_DEFAULT_ACCOUNT_BYTECODE_HASH, + L2_EVM_SIMULATOR_BYTECODE_HASH, this.addresses.StateTransition.Verifier, this.addresses.BlobVersionedHashRetriever, +priorityTxMaxGasLimit, diff --git a/l1-contracts/src.ts/diamondCut.ts b/l1-contracts/src.ts/diamondCut.ts index c2a8e8728..99759fa46 100644 --- a/l1-contracts/src.ts/diamondCut.ts +++ b/l1-contracts/src.ts/diamondCut.ts @@ -35,6 +35,7 @@ export interface InitializeData { allowList: BigNumberish; l2BootloaderBytecodeHash: string; l2DefaultAccountBytecodeHash: string; + l2EvmSimulatorBytecodeHash: string; priorityTxMaxGasLimit: BigNumberish; } diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index ca18bc7e4..5955f0497 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -136,6 +136,7 @@ export interface ProposedUpgrade { factoryDeps: BytesLike[]; bootloaderHash: BytesLike; defaultAccountHash: BytesLike; + evmSimulatorHash: BytesLike; verifier: string; verifierParams: VerifierParams; l1ContractsUpgradeCalldata: BytesLike; @@ -187,6 +188,7 @@ function checkValidInitialCutHashParams( verifierParams: VerifierParams, l2BootloaderBytecodeHash: string, l2DefaultAccountBytecodeHash: string, + l2EvmSimulatorBytecodeHash: string, verifier: string, blobVersionedHashRetriever: string, priorityTxMaxGasLimit: number @@ -215,6 +217,9 @@ function checkValidInitialCutHashParams( if (l2DefaultAccountBytecodeHash === ethers.constants.HashZero) { throw new Error("L2 default account bytecode hash is zero"); } + if (l2EvmSimulatorBytecodeHash === ethers.constants.HashZero) { + throw new Error("L2 evm simulator bytecode hash is zero"); + } if (verifier === ethers.constants.AddressZero) { throw new Error("Verifier address is zero"); } @@ -234,6 +239,7 @@ export function compileInitialCutHash( verifierParams: VerifierParams, l2BootloaderBytecodeHash: string, l2DefaultAccountBytecodeHash: string, + l2EvmSimulatorBytecodeHash: string, verifier: string, blobVersionedHashRetriever: string, priorityTxMaxGasLimit: number, @@ -246,6 +252,7 @@ export function compileInitialCutHash( verifierParams, l2BootloaderBytecodeHash, l2DefaultAccountBytecodeHash, + l2EvmSimulatorBytecodeHash, verifier, blobVersionedHashRetriever, priorityTxMaxGasLimit @@ -279,6 +286,7 @@ export function compileInitialCutHash( verifierParams, l2BootloaderBytecodeHash, l2DefaultAccountBytecodeHash, + l2EvmSimulatorBytecodeHash, priorityTxMaxGasLimit, feeParams, blobVersionedHashRetriever, diff --git a/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol b/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol index 7a4badf5f..1ee6bcebd 100644 --- a/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol @@ -91,6 +91,7 @@ contract UpgradeLogicTest is DiamondCutTest { // zkPorterIsAvailable: false, l2BootloaderBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, l2DefaultAccountBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, + l2EvmSimulatorBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, priorityTxMaxGasLimit: 500000, // priority tx max L2 gas limit // initialProtocolVersion: 0, feeParams: FeeParams({ diff --git a/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol b/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol index 6c6d8a935..36cc60ac9 100644 --- a/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol @@ -75,9 +75,12 @@ contract ExecutorProofTest is Test { /// This test is based on a block generated in a local system. function test_Hashes() public { utilsFacet.util_setL2DefaultAccountBytecodeHash( - 0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0 + 0x0100058d1abd41a9984b37939862f99c18237dc6951c3d5a3d81593c798a8f81 ); - utilsFacet.util_setL2BootloaderBytecodeHash(0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a); + utilsFacet.util_setL2EvmSimulatorBytecodeHash( + 0x01000f196acd122635a752fcb275be0cc95fd3bba348c1d0908a517fe316418e + ); + utilsFacet.util_setL2BootloaderBytecodeHash(0x010008ddde4acc465cde1c420883701caadb41954567c0b4e3a0d1093a7afde7); utilsFacet.util_setZkPorterAvailability(false); IExecutor.CommitBatchInfo memory nextBatch = IExecutor.CommitBatchInfo({ @@ -86,7 +89,7 @@ contract ExecutorProofTest is Test { // ignored timestamp: 100, indexRepeatedStorageChanges: 84, - newStateRoot: 0x9cf7bb72401a56039ca097cabed20a72221c944ed9b0e515c083c04663ab45a6, + newStateRoot: 0x1df8761352f4d39602beaf619e6b7a96111d3a54550bcf21f2623860cf3ed3d8, // ignored numberOfLayer1Txs: 10, // ignored @@ -118,12 +121,16 @@ contract ExecutorProofTest is Test { ); assertEq( nextCommitment, - 0xa1dcde434352cda8e331e721232ff2d457d4074efae1e3d06ef5b10ffada0c9a, + 0x3073bec0e225ab1e393420a8458f386b76f6e7ba784a2fbcc0ea24a6fd6a32a1, "nextCommitment computation failed" ); - bytes32 prevCommitment = 0x6ebf945305689a8c3ac993df7f002d41d311a762cd6bf39bb054ead8d1f54404; + bytes32 prevCommitment = 0x8199d18dbc01ea80a635f515d6a12312daa1aa32b5404944477dcd41fd7b2bdf; uint256 result = executor.getBatchProofPublicInput(prevCommitment, nextCommitment); - assertEq(result, 0xAC7931F2C11013FC24963E41B86E5325A79F1150350CB41E4F0876A7, "getBatchProofPublicInput"); + assertEq( + result, + 4788207466353486800513567418370563062194355012625279630180492433957, + "getBatchProofPublicInput" + ); } } diff --git a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol index d81e9cc30..18fb220e4 100644 --- a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol @@ -67,7 +67,7 @@ contract ExecutorTest is Test { } function getGettersSelectors() public view returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](29); + bytes4[] memory selectors = new bytes4[](30); selectors[0] = getters.getVerifier.selector; selectors[1] = getters.getAdmin.selector; selectors[2] = getters.getPendingAdmin.selector; @@ -83,20 +83,21 @@ contract ExecutorTest is Test { selectors[12] = getters.storedBatchHash.selector; selectors[13] = getters.getL2BootloaderBytecodeHash.selector; selectors[14] = getters.getL2DefaultAccountBytecodeHash.selector; - selectors[15] = getters.getVerifierParams.selector; - selectors[16] = getters.isDiamondStorageFrozen.selector; - selectors[17] = getters.getPriorityTxMaxGasLimit.selector; - selectors[18] = getters.isEthWithdrawalFinalized.selector; - selectors[19] = getters.facets.selector; - selectors[20] = getters.facetFunctionSelectors.selector; - selectors[21] = getters.facetAddresses.selector; - selectors[22] = getters.facetAddress.selector; - selectors[23] = getters.isFunctionFreezable.selector; - selectors[24] = getters.isFacetFreezable.selector; - selectors[25] = getters.getTotalBatchesCommitted.selector; - selectors[26] = getters.getTotalBatchesVerified.selector; - selectors[27] = getters.getTotalBatchesExecuted.selector; - selectors[28] = getters.storedBlockHash.selector; + selectors[15] = getters.getL2EvmSimulatorBytecodeHash.selector; + selectors[16] = getters.getVerifierParams.selector; + selectors[17] = getters.isDiamondStorageFrozen.selector; + selectors[18] = getters.getPriorityTxMaxGasLimit.selector; + selectors[19] = getters.isEthWithdrawalFinalized.selector; + selectors[20] = getters.facets.selector; + selectors[21] = getters.facetFunctionSelectors.selector; + selectors[22] = getters.facetAddresses.selector; + selectors[23] = getters.facetAddress.selector; + selectors[24] = getters.isFunctionFreezable.selector; + selectors[25] = getters.isFacetFreezable.selector; + selectors[26] = getters.getTotalBatchesCommitted.selector; + selectors[27] = getters.getTotalBatchesVerified.selector; + selectors[28] = getters.getTotalBatchesExecuted.selector; + selectors[29] = getters.storedBlockHash.selector; return selectors; } @@ -177,6 +178,7 @@ contract ExecutorTest is Test { }), l2BootloaderBytecodeHash: dummyHash, l2DefaultAccountBytecodeHash: dummyHash, + l2EvmSimulatorBytecodeHash: dummyHash, priorityTxMaxGasLimit: 1000000, feeParams: defaultFeeParams(), blobVersionedHashRetriever: blobVersionedHashRetriever diff --git a/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol b/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol index 3b46d212a..008a93db4 100644 --- a/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol +++ b/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol @@ -210,7 +210,7 @@ library Utils { } function getGettersSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](30); + bytes4[] memory selectors = new bytes4[](31); selectors[0] = GettersFacet.getVerifier.selector; selectors[1] = GettersFacet.getAdmin.selector; selectors[2] = GettersFacet.getPendingAdmin.selector; @@ -226,21 +226,22 @@ library Utils { selectors[12] = GettersFacet.storedBatchHash.selector; selectors[13] = GettersFacet.getL2BootloaderBytecodeHash.selector; selectors[14] = GettersFacet.getL2DefaultAccountBytecodeHash.selector; - selectors[15] = GettersFacet.getVerifierParams.selector; - selectors[16] = GettersFacet.isDiamondStorageFrozen.selector; - selectors[17] = GettersFacet.getPriorityTxMaxGasLimit.selector; - selectors[18] = GettersFacet.isEthWithdrawalFinalized.selector; - selectors[19] = GettersFacet.facets.selector; - selectors[20] = GettersFacet.facetFunctionSelectors.selector; - selectors[21] = GettersFacet.facetAddresses.selector; - selectors[22] = GettersFacet.facetAddress.selector; - selectors[23] = GettersFacet.isFunctionFreezable.selector; - selectors[24] = GettersFacet.isFacetFreezable.selector; - selectors[25] = GettersFacet.getTotalBatchesCommitted.selector; - selectors[26] = GettersFacet.getTotalBatchesVerified.selector; - selectors[27] = GettersFacet.getTotalBatchesExecuted.selector; - selectors[28] = GettersFacet.getL2SystemContractsUpgradeTxHash.selector; - selectors[29] = GettersFacet.getChainId.selector; + selectors[15] = GettersFacet.getL2EvmSimulatorBytecodeHash.selector; + selectors[16] = GettersFacet.getVerifierParams.selector; + selectors[17] = GettersFacet.isDiamondStorageFrozen.selector; + selectors[18] = GettersFacet.getPriorityTxMaxGasLimit.selector; + selectors[19] = GettersFacet.isEthWithdrawalFinalized.selector; + selectors[20] = GettersFacet.facets.selector; + selectors[21] = GettersFacet.facetFunctionSelectors.selector; + selectors[22] = GettersFacet.facetAddresses.selector; + selectors[23] = GettersFacet.facetAddress.selector; + selectors[24] = GettersFacet.isFunctionFreezable.selector; + selectors[25] = GettersFacet.isFacetFreezable.selector; + selectors[26] = GettersFacet.getTotalBatchesCommitted.selector; + selectors[27] = GettersFacet.getTotalBatchesVerified.selector; + selectors[28] = GettersFacet.getTotalBatchesExecuted.selector; + selectors[29] = GettersFacet.getL2SystemContractsUpgradeTxHash.selector; + selectors[30] = GettersFacet.getChainId.selector; return selectors; } @@ -258,7 +259,7 @@ library Utils { } function getUtilsFacetSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](41); + bytes4[] memory selectors = new bytes4[](43); selectors[0] = UtilsFacet.util_setChainId.selector; selectors[1] = UtilsFacet.util_getChainId.selector; selectors[2] = UtilsFacet.util_setBridgehub.selector; @@ -277,29 +278,31 @@ library Utils { selectors[15] = UtilsFacet.util_getL2BootloaderBytecodeHash.selector; selectors[16] = UtilsFacet.util_setL2DefaultAccountBytecodeHash.selector; selectors[17] = UtilsFacet.util_getL2DefaultAccountBytecodeHash.selector; - selectors[18] = UtilsFacet.util_setPendingAdmin.selector; - selectors[19] = UtilsFacet.util_getPendingAdmin.selector; - selectors[20] = UtilsFacet.util_setAdmin.selector; - selectors[21] = UtilsFacet.util_getAdmin.selector; - selectors[22] = UtilsFacet.util_setValidator.selector; - selectors[23] = UtilsFacet.util_getValidator.selector; - selectors[24] = UtilsFacet.util_setZkPorterAvailability.selector; - selectors[25] = UtilsFacet.util_getZkPorterAvailability.selector; - selectors[26] = UtilsFacet.util_setStateTransitionManager.selector; - selectors[27] = UtilsFacet.util_getStateTransitionManager.selector; - selectors[28] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; - selectors[29] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; - selectors[30] = UtilsFacet.util_setFeeParams.selector; - selectors[31] = UtilsFacet.util_getFeeParams.selector; - selectors[32] = UtilsFacet.util_setProtocolVersion.selector; - selectors[33] = UtilsFacet.util_getProtocolVersion.selector; - selectors[34] = UtilsFacet.util_setIsFrozen.selector; - selectors[35] = UtilsFacet.util_getIsFrozen.selector; - selectors[36] = UtilsFacet.util_setTransactionFilterer.selector; - selectors[37] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; - selectors[38] = UtilsFacet.util_setTotalBatchesExecuted.selector; - selectors[39] = UtilsFacet.util_setL2LogsRootHash.selector; - selectors[40] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; + selectors[18] = UtilsFacet.util_getL2EvmSimulatorBytecodeHash.selector; + selectors[19] = UtilsFacet.util_setL2EvmSimulatorBytecodeHash.selector; + selectors[20] = UtilsFacet.util_setPendingAdmin.selector; + selectors[21] = UtilsFacet.util_getPendingAdmin.selector; + selectors[22] = UtilsFacet.util_setAdmin.selector; + selectors[23] = UtilsFacet.util_getAdmin.selector; + selectors[24] = UtilsFacet.util_setValidator.selector; + selectors[25] = UtilsFacet.util_getValidator.selector; + selectors[26] = UtilsFacet.util_setZkPorterAvailability.selector; + selectors[27] = UtilsFacet.util_getZkPorterAvailability.selector; + selectors[28] = UtilsFacet.util_setStateTransitionManager.selector; + selectors[29] = UtilsFacet.util_getStateTransitionManager.selector; + selectors[30] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; + selectors[31] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; + selectors[32] = UtilsFacet.util_setFeeParams.selector; + selectors[33] = UtilsFacet.util_getFeeParams.selector; + selectors[34] = UtilsFacet.util_setProtocolVersion.selector; + selectors[35] = UtilsFacet.util_getProtocolVersion.selector; + selectors[36] = UtilsFacet.util_setIsFrozen.selector; + selectors[37] = UtilsFacet.util_getIsFrozen.selector; + selectors[38] = UtilsFacet.util_setTransactionFilterer.selector; + selectors[39] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; + selectors[40] = UtilsFacet.util_setTotalBatchesExecuted.selector; + selectors[41] = UtilsFacet.util_setL2LogsRootHash.selector; + selectors[42] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; return selectors; } @@ -340,6 +343,7 @@ library Utils { verifierParams: makeVerifierParams(), l2BootloaderBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, l2DefaultAccountBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, + l2EvmSimulatorBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, priorityTxMaxGasLimit: 500000, feeParams: makeFeeParams(), blobVersionedHashRetriever: address(0x23746765237749923040872834) @@ -355,6 +359,7 @@ library Utils { verifierParams: makeVerifierParams(), l2BootloaderBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, l2DefaultAccountBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, + l2EvmSimulatorBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, priorityTxMaxGasLimit: 80000000, feeParams: makeFeeParams(), blobVersionedHashRetriever: address(0x23746765237749923040872834) diff --git a/l1-contracts/test/foundry/unit/concrete/Utils/UtilsFacet.sol b/l1-contracts/test/foundry/unit/concrete/Utils/UtilsFacet.sol index ce9e659a0..55c696b8e 100644 --- a/l1-contracts/test/foundry/unit/concrete/Utils/UtilsFacet.sol +++ b/l1-contracts/test/foundry/unit/concrete/Utils/UtilsFacet.sol @@ -80,6 +80,14 @@ contract UtilsFacet is ZkSyncHyperchainBase { return s.l2DefaultAccountBytecodeHash; } + function util_setL2EvmSimulatorBytecodeHash(bytes32 _l2EvmSimulatorBytecodeHash) external { + s.l2EvmSimulatorBytecodeHash = _l2EvmSimulatorBytecodeHash; + } + + function util_getL2EvmSimulatorBytecodeHash() external view returns (bytes32) { + return s.l2EvmSimulatorBytecodeHash; + } + function util_setPendingAdmin(address _pendingAdmin) external { s.pendingAdmin = _pendingAdmin; } diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol index 205752a9f..cac368a2b 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol @@ -100,6 +100,7 @@ contract InitializeTest is DiamondInitTest { ); assertEq(utilsFacet.util_getL2BootloaderBytecodeHash(), initializeData.l2BootloaderBytecodeHash); assertEq(utilsFacet.util_getL2DefaultAccountBytecodeHash(), initializeData.l2DefaultAccountBytecodeHash); + assertEq(utilsFacet.util_getL2EvmSimulatorBytecodeHash(), initializeData.l2EvmSimulatorBytecodeHash); assertEq(utilsFacet.util_getPriorityTxMaxGasLimit(), initializeData.priorityTxMaxGasLimit); assertEq( keccak256(abi.encode(utilsFacet.util_getFeeParams())), diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/ExecuteUpgrade.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/ExecuteUpgrade.t.sol index d09b6f204..71ccfc581 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/ExecuteUpgrade.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/ExecuteUpgrade.t.sol @@ -45,6 +45,7 @@ contract ExecuteUpgradeTest is AdminTest { factoryDeps: new bytes[](0), bootloaderHash: bytes32(0), defaultAccountHash: bytes32(0), + evmSimulatorHash: bytes32(0), verifier: address(0), verifierParams: verifierParams, l1ContractsUpgradeCalldata: hex"", diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol index 1d64711fe..948c79925 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol @@ -90,6 +90,10 @@ contract GettersFacetWrapper is GettersFacet { s.l2DefaultAccountBytecodeHash = _l2DefaultAccountBytecodeHash; } + function utils_setL2EvmSimulatorBytecodeHash(bytes32 _l2EvmSimulatorBytecodeHash) external { + s.l2EvmSimulatorBytecodeHash = _l2EvmSimulatorBytecodeHash; + } + function util_setVerifierParams(VerifierParams memory _verifierParams) external { s.__DEPRECATED_verifierParams = _verifierParams; } diff --git a/l1-contracts/test/unit_tests/executor_proof.spec.ts b/l1-contracts/test/unit_tests/executor_proof.spec.ts index 01ac20fc7..8bbb46df3 100644 --- a/l1-contracts/test/unit_tests/executor_proof.spec.ts +++ b/l1-contracts/test/unit_tests/executor_proof.spec.ts @@ -15,9 +15,11 @@ describe("Executor test", function () { /// This test is based on a block generated in a local system. it("Test hashes (Rollup)", async () => { - const bootloaderHash = "0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a"; - const aaHash = "0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0"; - const setResult = await executor.setHashes(aaHash, bootloaderHash); + const bootloaderHash = "0x010008bbde6fc402ea3a3d6cb15cb97e70245d3d4e48fb74362d4961b74c16b1"; + const aaHash = "0x0100058d9eee51f4b9e9a9ecb7fd7e8301e90bef018c2bd913ed36e583fec8c2"; + const evmSimulatorHash = "0x01000ccb740e2345754450eda583f59b31a346920a22f968dfcfc63feae303ee"; + + const setResult = await executor.setHashes(aaHash, bootloaderHash, evmSimulatorHash); const finish = await setResult.wait(); expect(finish.status == 1); diff --git a/l1-contracts/test/unit_tests/l2-upgrade.test.spec.ts b/l1-contracts/test/unit_tests/l2-upgrade.test.spec.ts index b5d97bf7d..96d00a531 100644 --- a/l1-contracts/test/unit_tests/l2-upgrade.test.spec.ts +++ b/l1-contracts/test/unit_tests/l2-upgrade.test.spec.ts @@ -416,6 +416,7 @@ describe.only("L2 upgrade test", function () { it("Should successfully perform an upgrade", async () => { const bootloaderHash = ethers.utils.hexlify(hashBytecode(ethers.utils.randomBytes(32))); const defaultAccountHash = ethers.utils.hexlify(hashBytecode(ethers.utils.randomBytes(32))); + const evmSimulatorHash = ethers.utils.hexlify(hashBytecode(ethers.utils.randomBytes(32))); const testnetVerifierFactory = await hardhat.ethers.getContractFactory("TestnetVerifier"); const testnetVerifierContract = await testnetVerifierFactory.deploy(); const newVerifier = testnetVerifierContract.address; @@ -435,6 +436,7 @@ describe.only("L2 upgrade test", function () { const upgrade = { bootloaderHash, defaultAccountHash, + evmSimulatorHash, verifier: newVerifier, verifierParams: newerVerifierParams, executeUpgradeTx: true, @@ -466,6 +468,7 @@ describe.only("L2 upgrade test", function () { // Now, we check that all the data was set as expected expect(await proxyGetters.getL2BootloaderBytecodeHash()).to.equal(bootloaderHash); expect(await proxyGetters.getL2DefaultAccountBytecodeHash()).to.equal(defaultAccountHash); + expect(await proxyGetters.getL2EvmSimulatorBytecodeHash()).to.equal(evmSimulatorHash); expect((await proxyGetters.getVerifier()).toLowerCase()).to.equal(newVerifier.toLowerCase()); expect(await proxyGetters.getProtocolVersion()).to.equal(addToProtocolVersion(initialProtocolVersion, 5, 0)); @@ -499,6 +502,7 @@ describe.only("L2 upgrade test", function () { expect(upgradeEvents[3].args.newBytecodeHash).to.eq(bootloaderHash); expect(upgradeEvents[4].name).to.eq("NewL2DefaultAccountBytecodeHash"); + expect(upgradeEvents[4].args.previousBytecodeHash).to.eq(L2_DEFAULT_ACCOUNT_BYTECODE_HASH); expect(upgradeEvents[4].args.newBytecodeHash).to.eq(defaultAccountHash); }); @@ -508,6 +512,7 @@ describe.only("L2 upgrade test", function () { const currentVerifierParams = await proxyGetters.getVerifierParams(); const currentBootloaderHash = await proxyGetters.getL2BootloaderBytecodeHash(); const currentL2DefaultAccountBytecodeHash = await proxyGetters.getL2DefaultAccountBytecodeHash(); + const currentL2EvmSimulatorBytecodeHash = await proxyGetters.getL2EvmSimulatorBytecodeHash(); const testnetVerifierFactory = await hardhat.ethers.getContractFactory("TestnetVerifier"); const testnetVerifierContract = await testnetVerifierFactory.deploy(); @@ -552,6 +557,7 @@ describe.only("L2 upgrade test", function () { // Now, we check that all the data was set as expected expect(await proxyGetters.getL2BootloaderBytecodeHash()).to.equal(currentBootloaderHash); expect(await proxyGetters.getL2DefaultAccountBytecodeHash()).to.equal(currentL2DefaultAccountBytecodeHash); + expect(await proxyGetters.getL2EvmSimulatorBytecodeHash()).to.equal(currentL2EvmSimulatorBytecodeHash); expect((await proxyGetters.getVerifier()).toLowerCase()).to.equal(newVerifier.toLowerCase()); expect(await proxyGetters.getProtocolVersion()).to.equal(addToProtocolVersion(initialProtocolVersion, 5, 1)); @@ -942,6 +948,7 @@ function buildProposeUpgrade(proposedUpgrade: PartialProposedUpgrade): ProposedU l2ProtocolUpgradeTx: buildL2CanonicalTransaction({ nonce: newProtocolVersion }), bootloaderHash: ethers.constants.HashZero, defaultAccountHash: ethers.constants.HashZero, + evmSimulatorHash: ethers.constants.HashZero, verifier: ethers.constants.AddressZero, verifierParams: buildVerifierParams({}), l1ContractsUpgradeCalldata: "0x", diff --git a/l1-contracts/test/unit_tests/proxy_test.spec.ts b/l1-contracts/test/unit_tests/proxy_test.spec.ts index e63abe0bb..712bde127 100644 --- a/l1-contracts/test/unit_tests/proxy_test.spec.ts +++ b/l1-contracts/test/unit_tests/proxy_test.spec.ts @@ -96,6 +96,7 @@ describe("Diamond proxy tests", function () { verifierParams: dummyVerifierParams, l2BootloaderBytecodeHash: "0x0100000000000000000000000000000000000000000000000000000000000000", l2DefaultAccountBytecodeHash: "0x0100000000000000000000000000000000000000000000000000000000000000", + l2EvmSimulatorBytecodeHash: "0x0100000000000000000000000000000000000000000000000000000000000000", priorityTxMaxGasLimit: 500000, // priority tx max L2 gas limit feeParams: defaultFeeParams(), blobVersionedHashRetriever: "0x0000000000000000000000000000000000000001", diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 18da55393..2f0d9d00f 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,50 +3,50 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x0100005d05a277543946914759aa4a6c403604b828f80d00b900c669c3d224e1", - "sourceCodeHash": "0x2e0e09d57a04bd1e722d8bf8c6423fdf3f8bca44e5e8c4f6684f987794be066e" + "bytecodeHash": "0x0100008316f1f5fba0ee0278c40250b93c59fb5edb75658bfd2d3eb59e46fc2e", + "sourceCodeHash": "0xc92c3beabb281421f2d6dc9e46f9ba83b9d46f55a4c1d6799dbec2f57c92b98a" }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010007c7bb63f64649098bf75f4baa588db20f445b4d20b7cca972d5d8f973ce", - "sourceCodeHash": "0x0f1213c4b95acb71f4ab5d4082cc1aeb2bd5017e1cccd46afc66e53268609d85" + "bytecodeHash": "0x010007df001042752bef1c06161539a67ddc09461f32c8ed33402739e75b5787", + "sourceCodeHash": "0xed45097b2eaa4e47cd83f6feb3671d44adb49bac64c267844e76b3444605be19" }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x0100004da9f3aa5e4febcc53522cb7ee6949369fde25dd79e977752b82b9fd5d", + "bytecodeHash": "0x0100004dd0d550356d61d5735f7eef2fc8658aebe72e236058242d08615f4840", "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x0100014fb4f05ae09288cbcf4fa7a09ca456910f6e69be5ac2c2dfc8d71d1576", + "bytecodeHash": "0x0100014f5d4ca4d3f10878213fdec3f6cecaa7ad5738385a5047084175690013", "sourceCodeHash": "0xc6f7cd8b21aae52ed3dd5083c09b438a7af142a4ecda6067c586770e8be745a5" }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010004e5d52d692822d5c54ac87de3297f39be0e4a6f72f2830ae5ac856684ee", - "sourceCodeHash": "0x82f81fbf5fb007a9cac97462d50907ca5d7a1af62d82d2645e093ed8647a5209" + "bytecodeHash": "0x0100075982594cce9610e02a1043cf1bc0c9163045e646ac5b91534708f1dc2e", + "sourceCodeHash": "0x83e503214f41dc6677a100378f48961d459750017fc03dc5b8227c2ddddeba8b" }, { "contractName": "Create2Factory", "bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x010000495bd172e90725e6bfafe73e36a288d616d4673f5347eeb819a78bf546", + "bytecodeHash": "0x01000049d9a335e68cabc6be9103b1ce5bb12bc061eb9174105eaa4a03f6a2b3", "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x0100055dba11508480be023137563caec69debc85f826cb3a4b68246a7cabe30", - "sourceCodeHash": "0xebffe840ebbd9329edb1ebff8ca50f6935e7dabcc67194a896fcc2e968d46dfb" + "bytecodeHash": "0x0100058de8a8fda78449f14bece247271bdbba5dc73fc96135c35a17ee4dd090", + "sourceCodeHash": "0xb41382ac3d04739da79e438ee977b535bfb1c10b0dd4766f88b954b10d2710be" }, { "contractName": "EmptyContract", @@ -55,60 +55,67 @@ "bytecodeHash": "0x010000078f32964c38fbd138a0369f4723f07ac6f4919c45ef738c18bf874ccd", "sourceCodeHash": "0xcac36c5afafbcff83601f4fbfdff660aa66d8c80ed97b9322d3011c1926b554d" }, + { + "contractName": "EvmGasManager", + "bytecodePath": "artifacts-zk/contracts-preprocessed/EvmGasManager.sol/EvmGasManager.json", + "sourceCodePath": "contracts-preprocessed/EvmGasManager.sol", + "bytecodeHash": "0x010000f9b3ed54d84d89d5b3088afc18abe188a0834a8f0753a4e9997fd53224", + "sourceCodeHash": "0x0af32498e20adb67fd8e1af9d47679757379793ec3330b93cf0d8726ca9e0b2a" + }, { "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x01000039785a8e0d342a49b6b6c6e156801b816434d93bee85d33f56d56b4f9a", + "bytecodeHash": "0x01000039220b93fa1579ef897a360af9528e8b2b9f11ee24a836e07f9869879d", "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x0100006f0f209c9e6d06b1327db1257b15fa7a8b9864ee5ccd12cd3f8bc40ac9", - "sourceCodeHash": "0xb39b5b81168653e0c5062f7b8e1d6d15a4e186df3317f192f0cb2fc3a74f5448" + "bytecodeHash": "0x010000cfaa9edbf2c7a4ee998090c0b7f78865f3df19811221a2d814a2eed9bb", + "sourceCodeHash": "0xe07e6543ac09b86042eff5f9067df6db10a4fc039ecb56098a882ee1019c3031" }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x010002955993e8ff8190e388e94a6bb791fbe9c6388e5011c52cb587a4ebf05e", + "bytecodeHash": "0x01000299cba1a466f8bf47bfb8e08a2fa82910f0b47dbb0c7ecc21722c6b9627", "sourceCodeHash": "0xa8768fdaac6d8804782f14e2a51bbe2b6be31dee9103b6d02d149ea8dc46eb6a" }, { "contractName": "L2BaseToken", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x01000103174a90beadc2cffe3e81bdb7b8a576174e888809c1953175fd3015b4", + "bytecodeHash": "0x01000103fb54b518a2f4f51a2831d1f15e5bacc93e1ae0c65220a78a7c1f9231", "sourceCodeHash": "0x8bdd2b4d0b53dba84c9f0af250bbaa2aad10b3de6747bba957f0bd3721090dfa" }, { "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x0100005da36075675b98f85fe90df11c1d526f6b12925da3a55a8b9c02aaac5f", + "bytecodeHash": "0x0100005ded615ffb3639db7834b9aa49fbd2380fb63e3d5edff7de979d659d7c", "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000d97de8c14cd36b1ce06cd7f44a09f6093ec8eb4041629c0fc2116d0c73", - "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" + "bytecodeHash": "0x010000d9db21a9ac5d34d5e70fb3fd9fd65a51df6f38abb18bfaa29e90600def", + "sourceCodeHash": "0xff1ab1ce15c2e54954077315618822ec8deaeaae79ba4e2899518b8713a7234e" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x010000470e396f376539289b7975b6866914a8a0994008a02987edac8be81db7", + "bytecodeHash": "0x0100004714b937ae2caf106ea1ce6c641c0e7d49076b9a9f3124d3a6249c841a", "sourceCodeHash": "0xd7161e2c8092cf57b43c6220bc605c0e7e540bddcde1af24e2d90f75633b098e" }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001a5eabf9e28288b7ab7e1db316148021347460cfb4314570956867d5af5", + "bytecodeHash": "0x010001a5edef8985cb8bf31be024c56392e8a52d42162b12656178ad4450c02d", "sourceCodeHash": "0xf308743981ef5cea2f7a3332b8e51695a5e47e811a63974437fc1cceee475e7a" }, { @@ -118,12 +125,19 @@ "bytecodeHash": "0x010000159b30cba9e2096353695b63ca5cbf566416a545a6bcb2ff2e4e672f98", "sourceCodeHash": "0xfcf4828bcc109dea5f88c38f428d9ac5e18d5a2767fa4909277802c7e38c1f93" }, + { + "contractName": "EvmInterpreter", + "bytecodePath": "contracts-preprocessed/artifacts/EvmInterpreter.yul.zbin", + "sourceCodePath": "contracts-preprocessed/EvmInterpreter.yul", + "bytecodeHash": "0x01000cdf5bb7dd8a97faf231a5e1e20f2fe308d6f200c3295c6e3629547cc4a4", + "sourceCodeHash": "0xe1133c2af9e4fc38e845f7fcc23f3cbab37b857013f55b6e7e9bdea28f331c40" + }, { "contractName": "CodeOracle", "bytecodePath": "contracts-preprocessed/precompiles/artifacts/CodeOracle.yul.zbin", "sourceCodePath": "contracts-preprocessed/precompiles/CodeOracle.yul", - "bytecodeHash": "0x01000023d652655672eafbb0adc385bd423a4a59f752a28f3dde16e74fa205e3", - "sourceCodeHash": "0x476063e7907f2b7a532c4da6f606fa07186b5a10d77af8fdd83dbea3d9f23f93" + "bytecodeHash": "0x01000027d78a39f1406a8eaa2f3e52f93641c58472c1c5d20c0dff0423162de1", + "sourceCodeHash": "0xf4b21b6712a6e2a001a1b8214ac15959e670bd0ff125984486a28e128cb8846d" }, { "contractName": "EcAdd", @@ -178,35 +192,35 @@ "contractName": "bootloader_test", "bytecodePath": "bootloader/build/artifacts/bootloader_test.yul.zbin", "sourceCodePath": "bootloader/build/bootloader_test.yul", - "bytecodeHash": "0x010003cbe67434b2848054322cbc311385851bbfbf70d17f92cd5f1f9836b25e", - "sourceCodeHash": "0x006fdf461899dec5fdb34301c23e6819eb93e275907cbfc67d73fccfb47cae68" + "bytecodeHash": "0x010003cbb1c6b1d380510e6e5cedbe4fd089ba2addabcc774ab882774aad6326", + "sourceCodeHash": "0x4cac84261775ca327cf22d8b7595e020d03415e7c13311a8601b81543da1d94d" }, { "contractName": "fee_estimate", "bytecodePath": "bootloader/build/artifacts/fee_estimate.yul.zbin", "sourceCodePath": "bootloader/build/fee_estimate.yul", - "bytecodeHash": "0x0100092d9b8ae575bca569f68fe60fbef1dc7d4aad6aa02cc4836f56afcf0a38", - "sourceCodeHash": "0x8a858319bac2924a3dee778218a7fe5e23898db0d87b02d7b783f94c5a02d257" + "bytecodeHash": "0x010009251e961ae39033a0364f8a7a9cd9c0b2f666b8cb0d1941f76e7993bd68", + "sourceCodeHash": "0xaa1836b407eae63dcac7b99cfb91defdbac7baa6f4c049f9118181fff58c37ec" }, { "contractName": "gas_test", "bytecodePath": "bootloader/build/artifacts/gas_test.yul.zbin", "sourceCodePath": "bootloader/build/gas_test.yul", - "bytecodeHash": "0x010008b3e1b8bdd393f2f8d6f5c994c8b9c287df7310dee75d0c52a245fc7cb1", - "sourceCodeHash": "0x89f5ad470f10e755fa57b82507518e571c24409a328bc33aeba26e9518ad1c3e" + "bytecodeHash": "0x010008abf2fc690f5de0eed9dc9e293a86c5e7bc5ddf3032976e51185366e221", + "sourceCodeHash": "0x8c224e2e3c6e7c516ce5fbbbe841ecaaeb239e15e24ac0f2f7b4a8a11e9d9dfa" }, { "contractName": "playground_batch", "bytecodePath": "bootloader/build/artifacts/playground_batch.yul.zbin", "sourceCodePath": "bootloader/build/playground_batch.yul", - "bytecodeHash": "0x01000933061c23d700f3f647c45068e22f5506ff33bb516ac13f11069b163986", - "sourceCodeHash": "0x769448c4fd2b65c43d758ca5f34dd29d9b9dd3000fd0ec89cffcaf8d365a64fd" + "bytecodeHash": "0x0100092b66980e7077c0343560010a03243b607f473a65939b252003501ffc9c", + "sourceCodeHash": "0x04d534fd13610e0a82531f4d2a466e5b6caf814a50fb3a1b0f7d77918f078d26" }, { "contractName": "proved_batch", "bytecodePath": "bootloader/build/artifacts/proved_batch.yul.zbin", "sourceCodePath": "bootloader/build/proved_batch.yul", - "bytecodeHash": "0x010008c3be57ae5800e077b6c2056d9d75ad1a7b4f0ce583407961cc6fe0b678", - "sourceCodeHash": "0x908bc6ddb34ef89b125e9637239a1149deacacd91255781d82a65a542a39036e" + "bytecodeHash": "0x010008bbde6fc402ea3a3d6cb15cb97e70245d3d4e48fb74362d4961b74c16b1", + "sourceCodeHash": "0x72564482cf4e0e31bd47dbd15729952dd6994508c06f5065c2106920ad8e5a33" } ] diff --git a/system-contracts/bootloader/bootloader.yul b/system-contracts/bootloader/bootloader.yul index e995e7151..a42f9c4ee 100644 --- a/system-contracts/bootloader/bootloader.yul +++ b/system-contracts/bootloader/bootloader.yul @@ -3126,7 +3126,7 @@ object "Bootloader" { assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") - assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") + // assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") assertEq(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") assertEq(getFactoryDepsBytesLength(innerTxDataOffset), 0, "factory deps non zero") @@ -3151,9 +3151,8 @@ object "Bootloader" { assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") - assertEq(getReserved0(innerTxDataOffset), 0, "reserved0 non zero") - assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") + //assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") assertEq(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") assertEq(getFactoryDepsBytesLength(innerTxDataOffset), 0, "factory deps non zero") @@ -3176,7 +3175,7 @@ object "Bootloader" { assertEq(getReserved0(innerTxDataOffset), 0, "reserved0 non zero") - assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") + //assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") assertEq(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") assertEq(getFactoryDepsBytesLength(innerTxDataOffset), 0, "factory deps non zero") @@ -3195,7 +3194,7 @@ object "Bootloader" { assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") assertEq(getReserved0(innerTxDataOffset), 0, "reserved0 non zero") - assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") + //assertEq(getReserved1(innerTxDataOffset), 0, "reserved1 non zero") assertEq(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") } diff --git a/system-contracts/contracts/AccountCodeStorage.sol b/system-contracts/contracts/AccountCodeStorage.sol index 4c55279c4..15ccdd9ce 100644 --- a/system-contracts/contracts/AccountCodeStorage.sol +++ b/system-contracts/contracts/AccountCodeStorage.sol @@ -115,6 +115,9 @@ contract AccountCodeStorage is IAccountCodeStorage { // so set `keccak256("")` as a code hash. The EVM has the same behavior. else if (Utils.isContractConstructing(codeHash)) { codeHash = EMPTY_STRING_KECCAK; + } + else if (Utils.isCodeHashEVM(codeHash)) { + codeHash = DEPLOYER_SYSTEM_CONTRACT.evmCodeHash(account); } return codeHash; @@ -145,4 +148,10 @@ contract AccountCodeStorage is IAccountCodeStorage { codeSize = Utils.bytecodeLenInBytes(codeHash); } } + + /// @notice Method for detecting whether an address is an EVM contract + function isAccountEVM(address _addr) external view override returns (bool) { + bytes32 bytecodeHash = getRawCodeHash(_addr); + return Utils.isCodeHashEVM(bytecodeHash); + } } diff --git a/system-contracts/contracts/BootloaderUtilities.sol b/system-contracts/contracts/BootloaderUtilities.sol index 4fd38da74..94c6663a4 100644 --- a/system-contracts/contracts/BootloaderUtilities.sol +++ b/system-contracts/contracts/BootloaderUtilities.sol @@ -59,7 +59,9 @@ contract BootloaderUtilities is IBootloaderUtilities { encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. @@ -148,7 +150,9 @@ contract BootloaderUtilities is IBootloaderUtilities { bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // solhint-disable-next-line func-named-parameters encodedFixedLengthParams = bytes.concat( @@ -246,7 +250,9 @@ contract BootloaderUtilities is IBootloaderUtilities { bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // solhint-disable-next-line func-named-parameters encodedFixedLengthParams = bytes.concat( diff --git a/system-contracts/contracts/Constants.sol b/system-contracts/contracts/Constants.sol index 00145480b..dab28bfdc 100644 --- a/system-contracts/contracts/Constants.sol +++ b/system-contracts/contracts/Constants.sol @@ -14,6 +14,7 @@ import {ICompressor} from "./interfaces/ICompressor.sol"; import {IComplexUpgrader} from "./interfaces/IComplexUpgrader.sol"; import {IBootloaderUtilities} from "./interfaces/IBootloaderUtilities.sol"; import {IPubdataChunkPublisher} from "./interfaces/IPubdataChunkPublisher.sol"; +import {IEvmGasManager} from "./interfaces/IEvmGasManager.sol"; /// @dev All the system contracts introduced by ZKsync have their addresses /// started from 2^15 in order to avoid collision with Ethereum precompiles. @@ -34,6 +35,7 @@ address constant ECADD_SYSTEM_CONTRACT = address(0x06); address constant ECMUL_SYSTEM_CONTRACT = address(0x07); address constant ECPAIRING_SYSTEM_CONTRACT = address(0x08); +address constant CODE_ORACLE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x12); /// @dev The number of gas that need to be spent for a single byte of pubdata regardless of the pubdata price. /// This variable is used to ensure the following: @@ -83,6 +85,7 @@ address constant EVENT_WRITER_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x0d) ICompressor constant COMPRESSOR_CONTRACT = ICompressor(address(SYSTEM_CONTRACTS_OFFSET + 0x0e)); IComplexUpgrader constant COMPLEX_UPGRADER_CONTRACT = IComplexUpgrader(address(SYSTEM_CONTRACTS_OFFSET + 0x0f)); +IEvmGasManager constant EVM_GAS_MANAGER = IEvmGasManager(address(SYSTEM_CONTRACTS_OFFSET + 0x13)); IPubdataChunkPublisher constant PUBDATA_CHUNK_PUBLISHER = IPubdataChunkPublisher( address(SYSTEM_CONTRACTS_OFFSET + 0x11) @@ -102,6 +105,9 @@ bytes32 constant CREATE2_PREFIX = 0x2020dba91b30cc0006188af794c2fb30dd8520db7e2c /// @dev keccak256("zksyncCreate") bytes32 constant CREATE_PREFIX = 0x63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23; +/// @dev Prefix used during derivation of account addresses using CREATE2 within the EVM +bytes1 constant CREATE2_EVM_PREFIX = 0xff; + /// @dev Each state diff consists of 156 bytes of actual data and 116 bytes of unused padding, needed for circuit efficiency. uint256 constant STATE_DIFF_ENTRY_SIZE = 272; diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 8e9bda169..15656eda8 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +// solhint-disable reason-string, gas-custom-errors, func-named-parameters + pragma solidity 0.8.24; import {ImmutableData} from "./interfaces/IImmutableSimulator.sol"; @@ -26,6 +28,39 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { /// @dev For EOA and simple contracts (i.e. not accounts) this value is 0. mapping(address => AccountInfo) internal accountInfo; + enum EvmContractState { + None, + ConstructorPending, + ConstructorCalled, + Deployed + } + + uint256 private constant EVM_HASHES_PREFIX = 1 << 254; + + uint256 public constructorReturnGas; + + modifier onlySystemEvm() { + require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM(msg.sender), "only system evm"); + require(SystemContractHelper.isSystemCall(), "This method require system call flag"); + _; + } + + function setDeployedCode(uint256 constructorGasLeft, bytes calldata paddedNewDeployedCode) external onlySystemEvm { + require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM(msg.sender)); + + uint256 bytecodeLen = uint256(bytes32(paddedNewDeployedCode[:32])); + bytes memory trueBytecode = paddedNewDeployedCode[32:32 + bytecodeLen]; + + _setEvmCodeHash(msg.sender, keccak256(trueBytecode)); + constructorReturnGas = constructorGasLeft; + + // ToDO: use efficient call + KNOWN_CODE_STORAGE_CONTRACT.publishEVMBytecode(paddedNewDeployedCode); + + bytes32 codeHash = Utils.hashEVMBytecode(paddedNewDeployedCode); + ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructedCodeHash(msg.sender, codeHash); + } + modifier onlySelf() { if (msg.sender != address(this)) { revert Unauthorized(msg.sender); @@ -33,6 +68,10 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { _; } + function evmCodeHash(address _address) external view returns (bytes32 _hash) { + _hash = _getEvmCodeHash(_address); + } + /// @notice Returns information about a certain account. function getAccountInfo(address _address) external view returns (AccountInfo memory info) { return accountInfo[_address]; @@ -157,6 +196,39 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { return createAccount(_salt, _bytecodeHash, _input, AccountAbstractionVersion.None); } + function createEVM(bytes calldata _initCode) external payable override returns (address) { + // If the account is an EOA, use the min nonce. If it's a contract, use deployment nonce + // Subtract 1 for EOA since the nonce has already been incremented for this transaction + + uint256 deploymentNonce = NONCE_HOLDER_SYSTEM_CONTRACT.getDeploymentNonce(msg.sender); + if ((msg.sender != tx.origin) && deploymentNonce == 0) { + NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); + } + + uint256 senderNonce = msg.sender == tx.origin + ? NONCE_HOLDER_SYSTEM_CONTRACT.getMinNonce(msg.sender) - 1 + : NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); + address newAddress = Utils.getNewAddressCreateEVM(msg.sender, senderNonce); + _evmDeployOnAddress(newAddress, _initCode); + return newAddress; + } + + /// @notice Deploys an EVM contract using address derivation of EVM's `CREATE2` opcode + /// @param _salt The CREATE2 salt + /// @param _initCode The init code for the contract + /// Note: this method may be callable only in system mode, + /// that is checked in the `createAccount` by `onlySystemCall` modifier. + function create2EVM(bytes32 _salt, bytes calldata _initCode) external payable override returns (address) { + // No collision is possible with the zksync's non-EVM CREATE2, since + // the prefixes are different + bytes32 bytecodeHash = EfficientCall.keccak(_initCode); + address newAddress = Utils.getNewAddressCreate2EVM(msg.sender, _salt, bytecodeHash); + + _evmDeployOnAddress(newAddress, _initCode); + + return newAddress; + } + /// @notice Deploys a contract account with similar address derivation rules to the EVM's `CREATE2` opcode. /// @param _salt The CREATE2 salt /// @param _bytecodeHash The correctly formatted hash of the bytecode. @@ -284,7 +356,14 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { revert NonEmptyAccount(); } - _performDeployOnAddress(_bytecodeHash, _newAddress, _aaVersion, _input); + _performDeployOnAddress(_bytecodeHash, _newAddress, _aaVersion, _input, true); + } + + function _evmDeployOnAddress(address _newAddress, bytes calldata _initCode) internal { + // Unfortunately we can not provide revert reason as it would break EVM compatibility + require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(_newAddress) == 0x0); + require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(_newAddress))) == 0x0); + _performDeployOnAddressEVM(_newAddress, AccountAbstractionVersion.None, _initCode); } /// @notice Deploy a certain bytecode on the address. @@ -296,7 +375,8 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { bytes32 _bytecodeHash, address _newAddress, AccountAbstractionVersion _aaVersion, - bytes calldata _input + bytes calldata _input, + bool _callConstructor ) internal { _ensureBytecodeIsKnown(_bytecodeHash); @@ -312,10 +392,50 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { _bytecodeHash: _bytecodeHash, _input: _input, _isSystem: false, - _callConstructor: true + _callConstructor: _callConstructor }); } + function convertToConstructorEVMInput(bytes calldata _input) internal pure returns (bytes memory) { + // With how the contracts work, the calldata to the constructor must be an ABI-encoded `bytes`. + // This means that it should also contain offset as well as length + uint256 _fullLength = _input.length; + bytes memory extendedInput = new bytes(_input.length + 64); + + assembly { + // "Offset" for the calldata + mstore(add(extendedInput, 0x20), 0x20) + // "Length" + mstore(add(extendedInput, 0x40), _fullLength) + + calldatacopy(add(extendedInput, 0x60), _input.offset, _fullLength) + } + + return extendedInput; + } + + /// @notice Deploy a certain bytecode on the address. + /// @param _newAddress The address of the contract to be deployed. + /// @param _aaVersion The version of the account abstraction protocol to use. + /// @param _input The constructor calldata. + function _performDeployOnAddressEVM( + address _newAddress, + AccountAbstractionVersion _aaVersion, + bytes calldata _input + ) internal { + AccountInfo memory newAccountInfo; + newAccountInfo.supportedAAVersion = _aaVersion; + // Accounts have sequential nonces by default. + newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential; + _storeAccountInfo(_newAddress, newAccountInfo); + + // Note, that for contracts the "nonce" is set as deployment nonce. + NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(_newAddress); + + // When constructing they just get the intrepeter bytecode hash in consutrcting mode + _constructEVMContract(msg.sender, _newAddress, _input); + } + /// @notice Check that bytecode hash is marked as known on the `KnownCodeStorage` system contracts function _ensureBytecodeIsKnown(bytes32 _bytecodeHash) internal view { uint256 knownCodeMarker = KNOWN_CODE_STORAGE_CONTRACT.getMarker(_bytecodeHash); @@ -384,4 +504,63 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { emit ContractDeployed(_sender, _bytecodeHash, _newAddress); } + + function _constructEVMContract(address _sender, address _newAddress, bytes calldata _input) internal { + // FIXME: this is a temporary limitation. + // To be removed in the future + require(_input.length > 0); + + constructorReturnGas = 0; + + uint256 value = msg.value; + // 1. Transfer the balance to the new address on the constructor call. + if (value > 0) { + BASE_TOKEN_SYSTEM_CONTRACT.transferFromTo(address(this), _newAddress, value); + } + + // 2. Set the constructed code hash on the account + _storeConstructingByteCodeHashOnAddress( + _newAddress, + // Dummy EVM bytecode hash just to call simulator. + // The second byte is `0x01` to indicate that it is being constructed. + bytes32(0x0201000000000000000000000000000000000000000000000000000000000000) + ); + + // 3. Call the constructor on behalf of the account + if (value > 0) { + // Safe to cast value, because `msg.value` <= `uint128.max` due to `MessageValueSimulator` invariant + SystemContractHelper.setValueForNextFarCall(uint128(value)); + } + + bool success = EfficientCall.rawMimicCall({ + _gas: uint32(gasleft()), + _address: _newAddress, + _data: _input, + _whoToMimic: msg.sender, + _isConstructor: true, + _isSystem: false + }); + + if (!success) { + EfficientCall.propagateRevert(); + } + + bytes32 codeHash = _getEvmCodeHash(_newAddress); + require(codeHash != 0x0, "The code hash must be set after the constructor call"); + emit ContractDeployed(_sender, codeHash, _newAddress); + } + + function _setEvmCodeHash(address _address, bytes32 _hash) internal { + assembly { + let slot := or(EVM_HASHES_PREFIX, _address) + sstore(slot, _hash) + } + } + + function _getEvmCodeHash(address _address) internal view returns (bytes32 _hash) { + assembly { + let slot := or(EVM_HASHES_PREFIX, _address) + _hash := sload(slot) + } + } } diff --git a/system-contracts/contracts/DefaultAccount.sol b/system-contracts/contracts/DefaultAccount.sol index 40a38e49b..78197856f 100644 --- a/system-contracts/contracts/DefaultAccount.sol +++ b/system-contracts/contracts/DefaultAccount.sol @@ -139,6 +139,12 @@ contract DefaultAccount is IAccount { bytes calldata data = _transaction.data; uint32 gas = Utils.safeCastToU32(gasleft()); + // TODO: if possible, maybe implement some way to avoid memory copying here. + if ((_transaction.reserved[1] != 0) && (to == address(0))) { + DEPLOYER_SYSTEM_CONTRACT.createEVM{value: value}(data); + return; + } + // Note, that the deployment method from the deployer contract can only be called with a "systemCall" flag. bool isSystemCall; if (to == address(DEPLOYER_SYSTEM_CONTRACT) && data.length >= 4) { diff --git a/system-contracts/contracts/EvmGasManager.sol b/system-contracts/contracts/EvmGasManager.sol new file mode 100644 index 000000000..398735247 --- /dev/null +++ b/system-contracts/contracts/EvmGasManager.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT + +// solhint-disable reason-string, gas-custom-errors + +pragma solidity ^0.8.0; + +import "./libraries/Utils.sol"; + +import {ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT} from "./Constants.sol"; +import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; + +// We consider all the contracts (including system ones) as warm. +uint160 constant PRECOMPILES_END = 0xffff; + +uint256 constant INF_PASS_GAS = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + +// Transient storage prefixes +uint256 constant IS_ACCOUNT_EVM_PREFIX = 1 << 255; +uint256 constant IS_ACCOUNT_WARM_PREFIX = 1 << 254; +uint256 constant IS_SLOT_WARM_PREFIX = 1 << 253; +uint256 constant EVM_STACK_SLOT = 2; + +contract EvmGasManager { + modifier onlySystemEvm() { + // cache use is safe since we do not support SELFDESTRUCT + uint256 transient_slot = IS_ACCOUNT_EVM_PREFIX | uint256(uint160(msg.sender)); + bool isEVM; + assembly { + isEVM := tload(transient_slot) + } + + if (!isEVM) { + bytes32 bytecodeHash = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(msg.sender); + isEVM = Utils.isCodeHashEVM(bytecodeHash); + if (isEVM) { + if (!Utils.isContractConstructing(bytecodeHash)) { + assembly { + tstore(transient_slot, isEVM) + } + } + } + } + + require(isEVM, "only system evm"); + require(SystemContractHelper.isSystemCall(), "This method requires system call flag"); + _; + } + + /* + returns true if the account was already warm + */ + function warmAccount(address account) external payable onlySystemEvm returns (bool wasWarm) { + if (uint160(account) < PRECOMPILES_END) return true; + + uint256 transient_slot = IS_ACCOUNT_WARM_PREFIX | uint256(uint160(account)); + + assembly { + wasWarm := tload(transient_slot) + } + + if (!wasWarm) { + assembly { + tstore(transient_slot, 1) + } + } + } + + function isSlotWarm(uint256 _slot) external view returns (bool isWarm) { + uint256 prefix = IS_SLOT_WARM_PREFIX | uint256(uint160(msg.sender)); + uint256 transient_slot; + assembly { + mstore(0, prefix) + mstore(0x20, _slot) + transient_slot := keccak256(0, 64) + } + + assembly { + isWarm := tload(transient_slot) + } + } + + function warmSlot( + uint256 _slot, + uint256 _currentValue + ) external payable onlySystemEvm returns (bool isWarm, uint256 originalValue) { + uint256 prefix = IS_SLOT_WARM_PREFIX | uint256(uint160(msg.sender)); + uint256 transient_slot; + assembly { + mstore(0, prefix) + mstore(0x20, _slot) + transient_slot := keccak256(0, 64) + } + + assembly { + isWarm := tload(transient_slot) + } + + if (isWarm) { + assembly { + originalValue := tload(add(transient_slot, 1)) + } + } else { + originalValue = _currentValue; + + assembly { + tstore(transient_slot, 1) + tstore(add(transient_slot, 1), originalValue) + } + } + } + + /* + + The flow is the following: + + When conducting call: + 1. caller calls to an EVM contract pushEVMFrame with the corresponding gas + 2. callee calls consumeEvmFrame to get the gas and determine if a call is static + 3. calleer calls popEVMFrame to remove the frame + */ + + function pushEVMFrame(uint256 passGas, bool isStatic) external onlySystemEvm { + assembly { + let stackDepth := add(tload(EVM_STACK_SLOT), 1) + tstore(EVM_STACK_SLOT, stackDepth) + let stackPointer := add(EVM_STACK_SLOT, mul(2, stackDepth)) + tstore(stackPointer, passGas) + tstore(add(stackPointer, 1), isStatic) + } + } + + function consumeEvmFrame() external onlySystemEvm returns (uint256 passGas, bool isStatic) { + uint256 stackDepth; + assembly { + stackDepth := tload(EVM_STACK_SLOT) + } + if (stackDepth == 0) return (INF_PASS_GAS, false); + + assembly { + let stackPointer := add(EVM_STACK_SLOT, mul(2, stackDepth)) + passGas := tload(stackPointer) + isStatic := tload(add(stackPointer, 1)) + tstore(stackPointer, INF_PASS_GAS) // mark as consumed + } + } + + function popEVMFrame() external onlySystemEvm { + uint256 stackDepth; + assembly { + stackDepth := tload(EVM_STACK_SLOT) + } + require(stackDepth != 0); + assembly { + tstore(EVM_STACK_SLOT, sub(stackDepth, 1)) + } + } +} diff --git a/system-contracts/contracts/EvmInterpreter.yul b/system-contracts/contracts/EvmInterpreter.yul new file mode 100644 index 000000000..b16daad6a --- /dev/null +++ b/system-contracts/contracts/EvmInterpreter.yul @@ -0,0 +1,5817 @@ +object "EVMInterpreter" { + code { + /// @dev This function is used to get the initCode. + /// @dev It assumes that the initCode has been passed via the calldata and so we use the pointer + /// to obtain the bytecode. + function getConstructorBytecode() { + let bytecodeLengthOffset := BYTECODE_OFFSET() + let bytecodeOffset := add(BYTECODE_OFFSET(), 32) + + loadCalldataIntoActivePtr() + + let size := getActivePtrDataSize() + mstore(bytecodeLengthOffset, size) + + copyActivePtrData(bytecodeOffset, 0, size) + } + + // Note that this function modifies EVM memory and does not restore it. It is expected that + // it is the last called function during execution. + function setDeployedCode(gasLeft, offset, len) { + // This error should never be triggered + // require(offset > 100, "Offset too small"); + + mstore(sub(offset, 100), 0xD9EB76B200000000000000000000000000000000000000000000000000000000) + mstore(sub(offset, 96), gasLeft) + mstore(sub(offset, 64), 0x40) + mstore(sub(offset, 32), len) + + let farCallAbi := getFarCallABI( + 0, + 0, + sub(offset, 100), + add(len, 100), + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := DEPLOYER_SYSTEM_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function padBytecode(offset, len) -> blobOffset, blobLen { + blobOffset := sub(offset, 32) + let trueLastByte := add(offset, len) + + mstore(blobOffset, len) + // clearing out additional bytes + mstore(trueLastByte, 0) + mstore(add(trueLastByte, 32), 0) + + blobLen := add(len, 32) + + if iszero(eq(mod(blobLen, 32), 0)) { + blobLen := add(blobLen, sub(32, mod(blobLen, 32))) + } + + // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd + if iszero(eq(mod(blobLen, 64), 32)) { + blobLen := add(blobLen, 32) + } + } + + function validateCorrectBytecode(offset, len, gasToReturn) -> returnGas { + if len { + let firstByte := shr(248, mload(offset)) + if eq(firstByte, 0xEF) { + revert(0, 0) + } + } + + let gasForCode := mul(len, 200) + returnGas := chargeGas(gasToReturn, gasForCode) + } + + function ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008002 + } + + function NONCE_HOLDER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008003 + } + + function DEPLOYER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008006 + } + + function CODE_ORACLE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008012 + } + + function EVM_GAS_MANAGER_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008013 + } + + function DEBUG_SLOT_OFFSET() -> offset { + offset := mul(32, 32) // TODO cleanup + } + + function LAST_RETURNDATA_SIZE_OFFSET() -> offset { + offset := add(DEBUG_SLOT_OFFSET(), mul(5, 32)) + } + + function STACK_OFFSET() -> offset { + offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 32) + } + + function BYTECODE_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1024, 32)) + } + + function INF_PASS_GAS() -> inf { + inf := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + } + + function MAX_POSSIBLE_BYTECODE() -> max { + max := 32000 + } + + function MEM_OFFSET() -> offset { + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) + } + + function MEM_OFFSET_INNER() -> offset { + offset := add(MEM_OFFSET(), 32) + } + + function MAX_POSSIBLE_MEM() -> max { + max := 0x100000 // 1MB + } + + function MAX_MEMORY_FRAME() -> max { + max := add(MEM_OFFSET_INNER(), MAX_POSSIBLE_MEM()) + } + + function MAX_UINT() -> max_uint { + max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + } + + // Essentially a NOP that will not get optimized away by the compiler + function $llvm_NoInline_llvm$_unoptimized() { + pop(1) + } + + function printHex(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebde) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() + } + + function printString(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdf) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() + } + + // It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 + function readIP(ip,maxAcceptablePos) -> opcode { + if gt(ip, maxAcceptablePos) { + revert(0, 0) + } + + opcode := and(mload(sub(ip, 31)), 0xff) + } + + function readBytes(start, maxAcceptablePos,length) -> value { + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) + } + value := shr(mul(8,sub(32,length)),mload(start)) + } + + function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, sub(position, 1))) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + let dup := mload(tempSp) + + newSp := add(sp, 0x20) + mstore(newSp, dup) + } + + function swapStackItem(sp, evmGas, position) -> evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, position)) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + + let s2 := mload(sp) + let s1 := mload(tempSp) + + mstore(sp, s1) + mstore(tempSp, s2) + } + + function popStackItem(sp, evmGasLeft) -> a, newSp { + // We can not return any error here, because it would break compatibility + if lt(sp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + a := mload(sp) + newSp := sub(sp, 0x20) + } + + function pushStackItem(sp, item, evmGasLeft) -> newSp { + if or(gt(sp, BYTECODE_OFFSET()), eq(sp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + newSp := add(sp, 0x20) + mstore(newSp, item) + } + + function popStackItemWithoutCheck(sp) -> a, newSp { + a := mload(sp) + newSp := sub(sp, 0x20) + } + + function pushStackItemWithoutCheck(sp, item) -> newSp { + newSp := add(sp, 0x20) + mstore(newSp, item) + } + + function popStackCheck(sp, evmGasLeft, numInputs) { + if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + } + + function pushStackCheck(sp, evmGasLeft, numInputs) { + if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + } + + function getCodeAddress() -> addr { + addr := verbatim_0i_1o("code_source") + } + + function loadReturndataIntoActivePtr() { + verbatim_0i_0o("return_data_ptr_to_active") + } + + function loadCalldataIntoActivePtr() { + verbatim_0i_0o("calldata_ptr_to_active") + } + + function getActivePtrDataSize() -> size { + size := verbatim_0i_1o("active_ptr_data_size") + } + + function copyActivePtrData(_dest, _source, _size) { + verbatim_3i_0o("active_ptr_data_copy", _dest, _source, _size) + } + + function ptrAddIntoActive(_dest) { + verbatim_1i_0o("active_ptr_add_assign", _dest) + } + + function ptrShrinkIntoActive(_dest) { + verbatim_1i_0o("active_ptr_shrink_assign", _dest) + } + + function _getRawCodeHash(account) -> hash { + mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) + mstore(4, account) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + hash := mload(0) + } + + function getIsStaticFromCallFlags() -> isStatic { + isStatic := verbatim_0i_1o("get_global::call_flags") + isStatic := iszero(iszero(and(isStatic, 0x04))) + } + + // Basically performs an extcodecopy, while returning the length of the bytecode. + function _fetchDeployedCode(addr, _offset, _len) -> codeLen { + codeLen := _fetchDeployedCodeWithDest(addr, 0, _len, _offset) + } + + // Basically performs an extcodecopy, while returning the length of the bytecode. + function _fetchDeployedCodeWithDest(addr, _offset, _len, dest) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + + if gt(_len, codeLen) { + _len := codeLen + } + + returndatacopy(dest, add(32,_offset), _len) + } + + // Returns the length of the bytecode. + function _fetchDeployedCodeLen(addr) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + switch iszero(success) + case 1 { + // The code oracle call can only fail in the case where the contract + // we are querying is the current one executing and it has not yet been + // deployed, i.e., if someone calls codesize (or extcodesize(address())) + // inside the constructor. In that case, code length is zero. + codeLen := 0 + } + default { + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + } + } + + function getDeployedBytecode() { + let codeLen := _fetchDeployedCode( + getCodeAddress(), + add(BYTECODE_OFFSET(), 32), + MAX_POSSIBLE_BYTECODE() + ) + + mstore(BYTECODE_OFFSET(), codeLen) + } + + function consumeEvmFrame() -> passGas, isStatic, callerEVM { + // function consumeEvmFrame() external returns (uint256 passGas, bool isStatic) + mstore(0, 0x04C14E9E00000000000000000000000000000000000000000000000000000000) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // Should never happen + revert(0, 0) + } + + returndatacopy(0,0,64) + + passGas := mload(0) + isStatic := mload(32) + + if iszero(eq(passGas, INF_PASS_GAS())) { + callerEVM := true + } + } + + function chargeGas(prevGas, toCharge) -> gasRemaining { + if lt(prevGas, toCharge) { + revertWithGas(0) + } + + gasRemaining := sub(prevGas, toCharge) + } + + function getMax(a, b) -> max { + max := b + if gt(a, b) { + max := a + } + } + + function bitLength(n) -> bitLen { + for { } gt(n, 0) { } { // while(n > 0) + if iszero(n) { + bitLen := 1 + break + } + n := shr(1, n) + bitLen := add(bitLen, 1) + } + } + + function bitMaskFromBytes(nBytes) -> bitMask { + bitMask := sub(exp(2, mul(nBytes, 8)), 1) // 2**(nBytes*8) - 1 + } + // The gas cost mentioned here is purely the cost of the contract, + // and does not consider the cost of the call itself nor the instructions + // to put the parameters in memory. + // Take into account MEM_OFFSET_INNER() when passing the argsOffset + function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { + switch addr + case 0x01 { // ecRecover + gasToCharge := 3000 + } + case 0x02 { // SHA2-256 + gasToCharge := 60 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(12, dataWordSize)) + } + case 0x03 { // RIPEMD-160 + gasToCharge := 600 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(120, dataWordSize)) + } + case 0x04 { // identity + gasToCharge := 15 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(3, dataWordSize)) + } + // [0; 31] (32 bytes) Bsize Byte size of B + // [32; 63] (32 bytes) Esize Byte size of E + // [64; 95] (32 bytes) Msize Byte size of M + /* + def calculate_iteration_count(exponent_length, exponent): + iteration_count = 0 + if exponent_length <= 32 and exponent == 0: iteration_count = 0 + elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + return max(iteration_count, 1) + def calculate_gas_cost(base_length, modulus_length, exponent_length, exponent): + multiplication_complexity = calculate_multiplication_complexity(base_length, modulus_length) + iteration_count = calculate_iteration_count(exponent_length, exponent) + return max(200, math.floor(multiplication_complexity * iteration_count / 3)) + */ + // modexp gas cost EIP below + // https://eips.ethereum.org/EIPS/eip-2565 + case 0x05 { // modexp + let mulComplex + let Bsize := mload(argsOffset) + let Esize := mload(add(argsOffset, 0x20)) + + { + let words := getMax(Bsize, mload(add(argsOffset, 0x40))) // shr(3, x) == x/8 + if and(lt(words, 64), eq(words, 64)){ + // if x <= 64: return x ** 2 + mulComplex := mul(words, words) + } + if and(and(lt(words, 1024), eq(words, 1024)), gt(words, 64)){ + // elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 + mulComplex := sub(add(shr(2, mul(words, words)), mul(96, words)), 3072) + } + if gt(words, 64) { + // else: return x ** 2 // 16 + 480 * x - 199680 + mulComplex := sub(add(shr(4, mul(words, words)), mul(480, words)), 199680) + } + } + + // [96 + Bsize; 96 + Bsize + Esize] E + let exponentFirst256, exponentIsZero, exponentBitLen + if or(lt(Esize, 32), eq(Esize, 32)) { + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentBitLen := bitLength(exponentFirst256) + exponentIsZero := iszero(and(exponentFirst256, bitMaskFromBytes(Esize))) + } + if gt(Esize, 32) { + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentIsZero := iszero(exponentFirst256) + let exponentNext + // This is done because the first 32bytes of the exponent were loaded + for { let i := 0 } lt(i, div(Esize, 32)) { i := add(i, 1) Esize := sub(Esize, 32) } { // check every 32bytes + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentNext := mload(add(add(add(argsOffset, 0x60), Bsize), add(mul(i, 32), 32))) + exponentBitLen := add(bitLength(exponentNext), mul(mul(32, 8), add(i, 1))) + if iszero(iszero(and(exponentNext, bitMaskFromBytes(Esize)))) { + exponentIsZero := false + } + } + } + + // if exponent_length <= 32 and exponent == 0: iteration_count = 0 + // return max(iteration_count, 1) + let iterationCount := 1 + // elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + if and(lt(Esize, 32), iszero(exponentIsZero)) { + iterationCount := sub(exponentBitLen, 1) + } + // elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + if gt(Esize, 32) { + iterationCount := add(mul(8, sub(Esize, 32)), sub(bitLength(and(exponentFirst256, MAX_UINT())), 1)) + } + + gasToCharge := getMax(200, div(mul(mulComplex, iterationCount), 3)) + } + // ecAdd ecMul ecPairing EIP below + // https://eips.ethereum.org/EIPS/eip-1108 + case 0x06 { // ecAdd + // The gas cost is fixed at 150. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 150 + } + case 0x07 { // ecMul + // The gas cost is fixed at 6000. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 6000 + } + // 35,000 * k + 45,000 gas, where k is the number of pairings being computed. + // The input must always be a multiple of 6 32-byte values. + case 0x08 { // ecPairing + gasToCharge := 45000 + let k := div(argsSize, 0xC0) // 0xC0 == 6*32 + gasToCharge := add(gasToCharge, mul(k, 35000)) + } + case 0x09 { // blake2f + // argsOffset[0; 3] (4 bytes) Number of rounds (big-endian uint) + gasToCharge := and(mload(argsOffset), 0xFFFFFFFF) // last 4bytes + } + default { + gasToCharge := 0 + } + } + + function checkMemOverflowByOffset(offset, evmGasLeft) { + if gt(offset, MAX_POSSIBLE_MEM()) { + mstore(0, evmGasLeft) + revert(0, 32) + } + } + + function checkMemOverflow(location, evmGasLeft) { + if gt(location, MAX_MEMORY_FRAME()) { + mstore(0, evmGasLeft) + revert(0, 32) + } + } + + function checkOverflow(data1, data2, evmGasLeft) { + if lt(add(data1, data2), data2) { + revertWithGas(evmGasLeft) + } + } + + function revertWithGas(evmGasLeft) { + mstore(0, evmGasLeft) + revert(0, 32) + } + + // This function can overflow, it is the job of the caller to ensure that it does not. + // The argument to this function is the offset into the memory region IN BYTES. + function expandMemory(newSize) -> gasCost { + let oldSizeInWords := mload(MEM_OFFSET()) + + // The add 31 here before dividing is there to account for misaligned + // memory expansions, where someone calls this with a newSize that is not + // a multiple of 32. For instance, if someone calls it with an offset of 33, + // the new size in words should be 2, not 1, but dividing by 32 will give 1. + // Adding 31 solves it. + let newSizeInWords := div(add(newSize, 31), 32) + + if gt(newSizeInWords, oldSizeInWords) { + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) + + mstore(MEM_OFFSET(), newSizeInWords) + } + } + + function isSlotWarm(key) -> isWarm { + mstore(0, 0x482D2E7400000000000000000000000000000000000000000000000000000000) + mstore(4, key) + + let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isWarm := mload(0) + } + + function warmSlot(key,currentValue) -> isWarm, originalValue { + mstore(0, 0xBDF7816000000000000000000000000000000000000000000000000000000000) + mstore(4, key) + mstore(36,currentValue) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 64) + + isWarm := mload(0) + originalValue := mload(32) + } + + function getFarCallABI( + dataOffset, + memoryPage, + dataStart, + dataLength, + gasPassed, + shardId, + forwardingMode, + isConstructorCall, + isSystemCall + ) -> ret { + let farCallAbi := 0 + farCallAbi := or(farCallAbi, dataOffset) + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gasPassed)) + farCallAbi := or(farCallAbi, shl(224, shardId)) + farCallAbi := or(farCallAbi, shl(232, forwardingMode)) + farCallAbi := or(farCallAbi, shl(248, 1)) + ret := farCallAbi + } + + function addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) -> newOffset,newSize { + newOffset := offset + newSize := size + if eq(isCallerEVM,1) { + // include gas + let previousValue := mload(sub(offset,32)) + mstore(sub(offset,32),evmGasLeft) + //mstore(sub(offset,32),previousValue) // Im not sure why this is needed, it was like this in the solidity code, + // but it appears to rewrite were we want to store the gas + + newOffset := sub(offset, 32) + newSize := add(size, 32) + } + } + + function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { + mstore(0, 0x8DB2BA7800000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 36, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 32) + isWarm := mload(0) + } + + function getRawNonce(addr) -> nonce { + mstore(0, 0x5AA9B6B500000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) + } + + function _isEVM(_addr) -> isEVM { + // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) + // function isAccountEVM(address _addr) external view returns (bool); + // IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( + // address(SYSTEM_CONTRACTS_OFFSET + 0x02) + // ); + + mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) + mstore(4, _addr) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isEVM := mload(0) + } + + function _pushEVMFrame(_passGas, _isStatic) { + // function pushEVMFrame(uint256 _passGas, bool _isStatic) external + + mstore(0, 0xEAD7715600000000000000000000000000000000000000000000000000000000) + mstore(4, _passGas) + mstore(36, _isStatic) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function _popEVMFrame() { + // function popEVMFrame() external + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + + mstore(0, 0xE467D2F000000000000000000000000000000000000000000000000000000000) + + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + // Each evm gas is 5 zkEVM one + function GAS_DIVISOR() -> gas_div { gas_div := 5 } + function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 + function OVERHEAD() -> overhead { overhead := 2000 } + + // From precompiles/CodeOracle + function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } + function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + + function _calcEVMGas(_zkevmGas) -> calczkevmGas { + calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) + } + + function getEVMGas() -> evmGas { + let _gas := gas() + let requiredGas := add(EVM_GAS_STIPEND(), OVERHEAD()) + + switch lt(_gas, requiredGas) + case 1 { + evmGas := 0 + } + default { + evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) + } + } + + function _getZkEVMGas(_evmGas, addr) -> zkevmGas { + zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } + } + + function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + let rtsz := returndatasize() + + loadReturndataIntoActivePtr() + + // if (rtsz > 31) + switch gt(rtsz, 31) + case 0 { + // Unexpected return data. + _gasLeft := 0 + _eraseReturndataPointer() + } + default { + returndatacopy(0, 0, 32) + _gasLeft := mload(0) + + // We copy as much returndata as possible without going over the + // returndata size. + switch lt(sub(rtsz, 32), _outputLen) + case 0 { returndatacopy(_outputOffset, 32, _outputLen) } + default { returndatacopy(_outputOffset, 32, sub(rtsz, 32)) } + + mstore(lastRtSzOffset, sub(rtsz, 32)) + + // Skip the returnData + ptrAddIntoActive(32) + } + } + + function _eraseReturndataPointer() { + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + let activePtrSize := getActivePtrDataSize() + ptrShrinkIntoActive(and(activePtrSize, 0xFFFFFFFF))// uint32(activePtrSize) + mstore(lastRtSzOffset, 0) + } + + function _saveReturndataAfterZkEVMCall() { + loadReturndataIntoActivePtr() + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + mstore(lastRtSzOffset, returndatasize()) + } + + function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp { + let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + + popStackCheck(oldSp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + if gt(gasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } + + let frameGasLeft + let success + switch _isEVM(addr) + case 0 { + // zkEVM native + gasToPass := _getZkEVMGas(gasToPass, addr) + let zkevmGasBefore := gas() + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPass, gasUsed) { + frameGasLeft := sub(gasToPass, gasUsed) + } + } + default { + _pushEVMFrame(gasToPass, true) + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, 0, 0) + + frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + _popEVMFrame() + } + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + + sp := pushStackItem(sp, success, evmGasLeft) + } + function capGas(evmGasLeft,oldGasToPass) -> gasToPass { + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + gasToPass := oldGasToPass + if gt(oldGasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } + } + + function getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) -> maxExpand{ + maxExpand := add(retOffset, retSize) + switch lt(maxExpand,add(argsOffset, argsSize)) + case 0 { + maxExpand := expandMemory(maxExpand) + } + default { + maxExpand := expandMemory(add(argsOffset, argsSize)) + } + } + + function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + + switch isStatic + case 0 { + switch is_evm + case 0 { + // zkEVM native + gasToPassNew := _getZkEVMGas(gasToPassNew, addr) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } + default { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(EVM_GAS_STIPEND(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + } + default { + if value { + revertWithGas(gasToPassNew) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } + } + + function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { + let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize + + popStackCheck(oldSp, evmGasLeft, 7) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + // static_gas = 0 + // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost + // code_execution_cost is the cost of the called code execution (limited by the gas parameter). + // If address is warm, then address_access_cost is 100, otherwise it is 2600. See section access sets. + // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. 2300 is thus removed from the cost, and also added to the gas input. + // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + if gt(value, 0) { + extraCost := add(extraCost,6700) + gasToPass := add(gasToPass,2300) + } + + if and(isAddrEmpty(addr), gt(value, 0)) { + extraCost := add(extraCost,25000) + } + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + argsOffset := add(argsOffset,MEM_OFFSET_INNER()) + retOffset := add(retOffset,MEM_OFFSET_INNER()) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset,retSize, evmGasLeft) + + checkMemOverflow(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflow(add(retOffset, retSize), evmGasLeft) + + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp,success, evmGasLeft) + } + + function delegateCall(oldSp, oldIsStatic, evmGasLeft) -> sp, isStatic, extraCost { + let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + + sp := oldSp + isStatic := oldIsStatic + + popStackCheck(sp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(sp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset, argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + if iszero(_isEVM(addr)) { + revertWithGas(evmGasLeft) + } + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + _pushEVMFrame(gasToPass, isStatic) + let success := delegatecall( + // We can not just pass all gas here to prevent overflow of zkEVM gas counter + EVM_GAS_STIPEND(), + addr, + add(MEM_OFFSET_INNER(), argsOffset), + argsSize, + 0, + 0 + ) + + let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + + _popEVMFrame() + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp, success, evmGasLeft) + } + + function _performStaticCall( + _calleeIsEVM, + _calleeGas, + _callee, + _inputOffset, + _inputLen, + _outputOffset, + _outputLen + ) -> success, _gasLeft { + switch _calleeIsEVM + case 0 { + // zkEVM native + _calleeGas := _getZkEVMGas(_calleeGas, _callee) + let zkevmGasBefore := gas() + success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) + + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + _gasLeft := 0 + if gt(_calleeGas, gasUsed) { + _gasLeft := sub(_calleeGas, gasUsed) + } + } + default { + _pushEVMFrame(_calleeGas, true) + success := staticcall(EVM_GAS_STIPEND(), _callee, _inputOffset, _inputLen, 0, 0) + + _gasLeft := _saveReturndataAfterEVMCall(_outputOffset, _outputLen) + _popEVMFrame() + } + } + + function isAddrEmpty(addr) -> isEmpty { + isEmpty := 0 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } + } + } + + function _fetchConstructorReturnGas() -> gasLeft { + mstore(0, 0x24E5AB4A00000000000000000000000000000000000000000000000000000000) + + let success := staticcall(gas(), DEPLOYER_SYSTEM_CONTRACT(), 0, 4, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + gasLeft := mload(0) + } + + function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt) -> result, evmGasLeft, addr { + pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) + + _eraseReturndataPointer() + + let gasForTheCall := capGas(evmGasLeftOld,INF_PASS_GAS()) + + if lt(selfbalance(),value) { + revertWithGas(evmGasLeftOld) + } + + offset := add(MEM_OFFSET_INNER(), offset) + + pushStackCheck(sp, evmGasLeftOld, 4) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20))) + + _pushEVMFrame(gasForTheCall, false) + + if isCreate2 { + // Create2EVM selector + mstore(sub(offset, 0x80), 0x4e96f4c0) + // salt + mstore(sub(offset, 0x60), salt) + // Where the arg starts (third word) + mstore(sub(offset, 0x40), 0x40) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x64), add(size, 0x64), 0, 32) + } + + + if iszero(isCreate2) { + // CreateEVM selector + mstore(sub(offset, 0x60), 0xff311601) + // Where the arg starts (second word) + mstore(sub(offset, 0x40), 0x20) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x44), add(size, 0x44), 0, 32) + } + + addr := mload(0) + + let gasLeft + switch result + case 0 { + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) + + _popEVMFrame() + + let back + + // skipping check since we pushed exactly 4 items earlier + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x20), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x40), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x60), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x80), back) + } + + function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { + let rest_bits := shl(3, len) + let upper_bits := sub(256, rest_bits) + let val_mask := shl(upper_bits, MAX_UINT()) + let val_masked := and(val, val_mask) + let dst_val := mload(dest) + let dst_mask := shr(rest_bits, MAX_UINT()) + let dst_masked := and(dst_val, dst_mask) + mstore(dest, or(val_masked, dst_masked)) + } + + function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { + let dest_addr := dest + let src_addr := src + let dest_end := add(dest, and(len, sub(0, 32))) + for { } lt(dest_addr, dest_end) {} { + mstore(dest_addr, mload(src_addr)) + dest_addr := add(dest_addr, 32) + src_addr := add(src_addr, 32) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + } + } + + function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { + let dest_end := add(dest, and(len, sub(0, 32))) + for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { + mstore(i, 0) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + } + } + + function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + popStackCheck(oldSp, evmGasLeft, 4) + addr, sp := popStackItemWithoutCheck(oldSp) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) + + // Gets the code from the addr + if and(iszero(iszero(_getRawCodeHash(addr))),gt(len,0)) { + pop(_fetchDeployedCodeWithDest(addr, offset, len,add(dest,MEM_OFFSET_INNER()))) + } + } + + function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size + + popStackCheck(oldSp, evmGasLeft, 3) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + let dynamicGas := add( + shr(4, add(size, 31)), + expandMemory(add(offset, size)) + ) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let result, addr + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0) + + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } + } + + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size, salt + + popStackCheck(oldSp, evmGasLeft, 4) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + salt, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // hash_cost = 6 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + evmGasLeft := chargeGas(evmGasLeft, add( + expandMemory(add(offset, size)), + shr(2, add(size, 31)) + )) + + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft,true,salt) + } + + + function simulate( + isCallerEVM, + evmGasLeft, + isStatic, + ) -> returnOffset, returnLen, retGasLeft { + + returnOffset := MEM_OFFSET_INNER() + returnLen := 0 + + // stack pointer - index to first stack element; empty stack = -1 + let sp := sub(STACK_OFFSET(), 32) + // instruction pointer - index to next instruction. Not called pc because it's an + // actual yul/evm instruction. + let ip := add(BYTECODE_OFFSET(), 32) + let opcode + + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + + for { } true { } { + opcode := readIP(ip,maxAcceptablePos) + + switch opcode + case 0x00 { // OP_STOP + break + } + case 0x01 { // OP_ADD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, add(a, b)) + ip := add(ip, 1) + } + case 0x02 { // OP_MUL + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mul(a, b)) + ip := add(ip, 1) + } + case 0x03 { // OP_SUB + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sub(a, b)) + ip := add(ip, 1) + } + case 0x04 { // OP_DIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, div(a, b)) + ip := add(ip, 1) + } + case 0x05 { // OP_SDIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sdiv(a, b)) + ip := add(ip, 1) + } + case 0x06 { // OP_MOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mod(a, b)) + ip := add(ip, 1) + } + case 0x07 { // OP_SMOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, smod(a, b)) + ip := add(ip, 1) + } + case 0x08 { // OP_ADDMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, addmod(a, b, N)) + ip := add(ip, 1) + } + case 0x09 { // OP_MULMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItem(sp, mulmod(a, b, N), evmGasLeft) + ip := add(ip, 1) + } + case 0x0A { // OP_EXP + evmGasLeft := chargeGas(evmGasLeft, 10) + + let a, exponent + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + exponent, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, exp(a, exponent)) + + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) + ip := add(ip, 1) + } + case 0x0B { // OP_SIGNEXTEND + evmGasLeft := chargeGas(evmGasLeft, 5) + + let b, x + + popStackCheck(sp, evmGasLeft, 2) + b, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, signextend(b, x)) + ip := add(ip, 1) + } + case 0x10 { // OP_LT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, lt(a, b)) + ip := add(ip, 1) + } + case 0x11 { // OP_GT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, gt(a, b)) + ip := add(ip, 1) + } + case 0x12 { // OP_SLT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, slt(a, b)) + ip := add(ip, 1) + } + case 0x13 { // OP_SGT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sgt(a, b)) + ip := add(ip, 1) + } + case 0x14 { // OP_EQ + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, eq(a, b)) + ip := add(ip, 1) + } + case 0x15 { // OP_ISZERO + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, iszero(a)) + ip := add(ip, 1) + } + case 0x16 { // OP_AND + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, and(a,b)) + ip := add(ip, 1) + } + case 0x17 { // OP_OR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, or(a,b)) + ip := add(ip, 1) + } + case 0x18 { // OP_XOR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, xor(a, b)) + ip := add(ip, 1) + } + case 0x19 { // OP_NOT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, not(a)) + ip := add(ip, 1) + } + case 0x1A { // OP_BYTE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i, x + + popStackCheck(sp, evmGasLeft, 2) + i, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, byte(i, x)) + ip := add(ip, 1) + } + case 0x1B { // OP_SHL + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shl(shift, value)) + ip := add(ip, 1) + } + case 0x1C { // OP_SHR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shr(shift, value)) + ip := add(ip, 1) + } + case 0x1D { // OP_SAR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sar(shift, value)) + ip := add(ip, 1) + } + case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) + + let offset, size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + let keccak := keccak256(add(MEM_OFFSET_INNER(), offset), size) + + // When an offset is first accessed (either read or write), memory may trigger + // an expansion, which costs gas. + // dynamicGas = 6 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + sp := pushStackItem(sp, keccak, evmGasLeft) + ip := add(ip, 1) + } + case 0x30 { // OP_ADDRESS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, address(), evmGasLeft) + ip := add(ip, 1) + } + case 0x31 { // OP_BALANCE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + sp := pushStackItemWithoutCheck(sp, balance(addr)) + ip := add(ip, 1) + } + case 0x32 { // OP_ORIGIN + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, origin(), evmGasLeft) + ip := add(ip, 1) + } + case 0x33 { // OP_CALLER + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, caller(), evmGasLeft) + ip := add(ip, 1) + } + case 0x34 { // OP_CALLVALUE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, callvalue(), evmGasLeft) + ip := add(ip, 1) + } + case 0x35 { // OP_CALLDATALOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i + + popStackCheck(sp, evmGasLeft, 1) + i, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, calldataload(i)) + ip := add(ip, 1) + } + case 0x36 { // OP_CALLDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, calldatasize(), evmGasLeft) + ip := add(ip, 1) + } + case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let destOffset, offset, size + + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(destOffset,size), evmGasLeft) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + ip := add(ip, 1) + + } + case 0x38 { // OP_CODESIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let bytecodeLen := mload(BYTECODE_OFFSET()) + sp := pushStackItem(sp, bytecodeLen, evmGasLeft) + ip := add(ip, 1) + } + case 0x39 { // OP_CODECOPY + + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dst, offset, len + + popStackCheck(sp, evmGasLeft, 3) + dst, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + dst := add(dst, MEM_OFFSET_INNER()) + offset := add(add(offset, BYTECODE_OFFSET()), 32) + + checkOverflow(dst,len, evmGasLeft) + checkOverflow(offset,len, evmGasLeft) + checkMemOverflow(add(dst, len), evmGasLeft) + // Check bytecode overflow + if gt(add(offset, len), sub(MEM_OFFSET(), 1)) { + revertWithGas(evmGasLeft) + } + + $llvm_AlwaysInline_llvm$_memcpy(dst, offset, len) + ip := add(ip, 1) + } + case 0x3A { // OP_GASPRICE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, gasprice(), evmGasLeft) + ip := add(ip, 1) + } + case 0x3B { // OP_EXTCODESIZE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + switch _isEVM(addr) + case 0 { sp := pushStackItemWithoutCheck(sp, extcodesize(addr)) } + default { sp := pushStackItemWithoutCheck(sp, _fetchDeployedCodeLen(addr)) } + ip := add(ip, 1) + } + case 0x3C { // OP_EXTCODECOPY + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) + ip := add(ip, 1) + } + case 0x3D { // OP_RETURNDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let rdz := mload(LAST_RETURNDATA_SIZE_OFFSET()) + sp := pushStackItem(sp, rdz, evmGasLeft) + ip := add(ip, 1) + } + case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dest, offset, len + popStackCheck(sp, evmGasLeft, 3) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,len, evmGasLeft) + if gt(add(offset, len), mload(LAST_RETURNDATA_SIZE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + // minimum_word_size = (size + 31) / 32 + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + checkMemOverflowByOffset(offset, evmGasLeft) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + ip := add(ip, 1) + } + case 0x3F { // OP_EXTCODEHASH + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + ip := add(ip, 1) + if iszero(addr) { + sp := pushStackItemWithoutCheck(sp, 0) + continue + } + sp := pushStackItemWithoutCheck(sp, extcodehash(addr)) + } + case 0x40 { // OP_BLOCKHASH + evmGasLeft := chargeGas(evmGasLeft, 20) + + let blockNumber + popStackCheck(sp, evmGasLeft, 1) + blockNumber, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, blockhash(blockNumber)) + ip := add(ip, 1) + } + case 0x41 { // OP_COINBASE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, coinbase(), evmGasLeft) + ip := add(ip, 1) + } + case 0x42 { // OP_TIMESTAMP + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, timestamp(), evmGasLeft) + ip := add(ip, 1) + } + case 0x43 { // OP_NUMBER + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, number(), evmGasLeft) + ip := add(ip, 1) + } + case 0x44 { // OP_PREVRANDAO + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, prevrandao(), evmGasLeft) + ip := add(ip, 1) + } + case 0x45 { // OP_GASLIMIT + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, gaslimit(), evmGasLeft) + ip := add(ip, 1) + } + case 0x46 { // OP_CHAINID + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, chainid(), evmGasLeft) + ip := add(ip, 1) + } + case 0x47 { // OP_SELFBALANCE + evmGasLeft := chargeGas(evmGasLeft, 5) + sp := pushStackItem(sp, selfbalance(), evmGasLeft) + ip := add(ip, 1) + } + case 0x48 { // OP_BASEFEE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, basefee(), evmGasLeft) + ip := add(ip, 1) + } + case 0x50 { // OP_POP + evmGasLeft := chargeGas(evmGasLeft, 2) + + let _y + + _y, sp := popStackItem(sp, evmGasLeft) + ip := add(ip, 1) + } + case 0x51 { // OP_MLOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset + + offset, sp := popStackItem(sp, evmGasLeft) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + let memValue := mload(add(MEM_OFFSET_INNER(), offset)) + sp := pushStackItemWithoutCheck(sp, memValue) + ip := add(ip, 1) + } + case 0x52 { // OP_MSTORE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x53 { // OP_MSTORE8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 1)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore8(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x54 { // OP_SLOAD + + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key, value, isWarm + + key, sp := popStackItem(sp, evmGasLeft) + + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { + evmGasLeft := chargeGas(evmGasLeft, 2000) + } + + value := sload(key) + + if iszero(wasWarm) { + let _wasW, _orgV := warmSlot(key, value) + } + + sp := pushStackItemWithoutCheck(sp,value) + ip := add(ip, 1) + } + case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value, gasSpent + + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + ip := add(ip, 1) + { + // Here it is okay to read before we charge since we known anyway that + // the context has enough funds to compensate at least for the read. + // Im not sure if we need this before: require(gasLeft > GAS_CALL_STIPEND); + let currentValue := sload(key) + let wasWarm, originalValue := warmSlot(key, currentValue) + + if eq(value, currentValue) { + continue + } + + if eq(originalValue, currentValue) { + gasSpent := 19900 + if originalValue { + gasSpent := 2800 + } + } + + if iszero(wasWarm) { + gasSpent := add(gasSpent, 2100) + } + } + + evmGasLeft := chargeGas(evmGasLeft, gasSpent) + sstore(key, value) + + } + // NOTE: We don't currently do full jumpdest validation + // (i.e. validating a jumpdest isn't in PUSH data) + case 0x56 { // OP_JUMP + evmGasLeft := chargeGas(evmGasLeft, 8) + + let counter + + counter, sp := popStackItem(sp, evmGasLeft) + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x57 { // OP_JUMPI + evmGasLeft := chargeGas(evmGasLeft, 10) + + let counter, b + + popStackCheck(sp, evmGasLeft, 2) + counter, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + if iszero(b) { + ip := add(ip, 1) + continue + } + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x58 { // OP_PC + evmGasLeft := chargeGas(evmGasLeft, 2) + ip := add(ip, 1) + + // PC = ip - 32 (bytecode size) - 1 (current instruction) + sp := pushStackItem(sp, sub(sub(ip, BYTECODE_OFFSET()), 33), evmGasLeft) + } + case 0x59 { // OP_MSIZE + evmGasLeft := chargeGas(evmGasLeft,2) + + let size + + size := mload(MEM_OFFSET()) + size := shl(5,size) + sp := pushStackItem(sp,size, evmGasLeft) + ip := add(ip, 1) + } + case 0x5A { // OP_GAS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, evmGasLeft, evmGasLeft) + ip := add(ip, 1) + } + case 0x5B { // OP_JUMPDEST + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x5C { // OP_TLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key + popStackCheck(sp, evmGasLeft, 1) + key, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, tload(key)) + ip := add(ip, 1) + } + case 0x5D { // OP_TSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + tstore(key, value) + ip := add(ip, 1) + } + case 0x5E { // OP_MCOPY + let destOffset, offset, size + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + checkMemOverflowByOffset(add(destOffset, size), evmGasLeft) + + expandMemory(add(destOffset, size)) + expandMemory(add(offset, size)) + + mcopy(add(destOffset, MEM_OFFSET_INNER()), add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0x5F { // OP_PUSH0 + evmGasLeft := chargeGas(evmGasLeft, 2) + + let value := 0 + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x60 { // OP_PUSH1 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,1) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x61 { // OP_PUSH2 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,2) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 2) + } + case 0x62 { // OP_PUSH3 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,3) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 3) + } + case 0x63 { // OP_PUSH4 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,4) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 4) + } + case 0x64 { // OP_PUSH5 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,5) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 5) + } + case 0x65 { // OP_PUSH6 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,6) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 6) + } + case 0x66 { // OP_PUSH7 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,7) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 7) + } + case 0x67 { // OP_PUSH8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,8) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 8) + } + case 0x68 { // OP_PUSH9 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,9) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 9) + } + case 0x69 { // OP_PUSH10 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,10) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 10) + } + case 0x6A { // OP_PUSH11 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,11) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 11) + } + case 0x6B { // OP_PUSH12 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,12) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 12) + } + case 0x6C { // OP_PUSH13 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,13) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 13) + } + case 0x6D { // OP_PUSH14 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,14) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 14) + } + case 0x6E { // OP_PUSH15 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,15) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 15) + } + case 0x6F { // OP_PUSH16 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,16) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 16) + } + case 0x70 { // OP_PUSH17 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,17) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 17) + } + case 0x71 { // OP_PUSH18 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,18) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 18) + } + case 0x72 { // OP_PUSH19 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,19) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 19) + } + case 0x73 { // OP_PUSH20 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,20) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 20) + } + case 0x74 { // OP_PUSH21 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,21) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 21) + } + case 0x75 { // OP_PUSH22 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,22) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 22) + } + case 0x76 { // OP_PUSH23 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,23) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 23) + } + case 0x77 { // OP_PUSH24 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,24) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 24) + } + case 0x78 { // OP_PUSH25 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,25) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 25) + } + case 0x79 { // OP_PUSH26 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,26) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 26) + } + case 0x7A { // OP_PUSH27 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,27) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 27) + } + case 0x7B { // OP_PUSH28 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,28) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 28) + } + case 0x7C { // OP_PUSH29 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,29) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 29) + } + case 0x7D { // OP_PUSH30 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,30) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 30) + } + case 0x7E { // OP_PUSH31 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,31) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 31) + } + case 0x7F { // OP_PUSH32 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,32) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 32) + } + case 0x80 { // OP_DUP1 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x81 { // OP_DUP2 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x82 { // OP_DUP3 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x83 { // OP_DUP4 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x84 { // OP_DUP5 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x85 { // OP_DUP6 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x86 { // OP_DUP7 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x87 { // OP_DUP8 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x88 { // OP_DUP9 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x89 { // OP_DUP10 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x8A { // OP_DUP11 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x8B { // OP_DUP12 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x8C { // OP_DUP13 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x8D { // OP_DUP14 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x8E { // OP_DUP15 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x8F { // OP_DUP16 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0x90 { // OP_SWAP1 + evmGasLeft := swapStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x91 { // OP_SWAP2 + evmGasLeft := swapStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x92 { // OP_SWAP3 + evmGasLeft := swapStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x93 { // OP_SWAP4 + evmGasLeft := swapStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x94 { // OP_SWAP5 + evmGasLeft := swapStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x95 { // OP_SWAP6 + evmGasLeft := swapStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x96 { // OP_SWAP7 + evmGasLeft := swapStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x97 { // OP_SWAP8 + evmGasLeft := swapStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x98 { // OP_SWAP9 + evmGasLeft := swapStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x99 { // OP_SWAP10 + evmGasLeft := swapStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x9A { // OP_SWAP11 + evmGasLeft := swapStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x9B { // OP_SWAP12 + evmGasLeft := swapStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x9C { // OP_SWAP13 + evmGasLeft := swapStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x9D { // OP_SWAP14 + evmGasLeft := swapStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x9E { // OP_SWAP15 + evmGasLeft := swapStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x9F { // OP_SWAP16 + evmGasLeft := swapStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0xA0 { // OP_LOG0 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + log0(add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0xA1 { // OP_LOG1 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 3) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 375) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1 + topic1, sp := popStackItemWithoutCheck(sp) + log1(add(offset, MEM_OFFSET_INNER()), size, topic1) + } + ip := add(ip, 1) + } + case 0xA2 { // OP_LOG2 + evmGasLeft := chargeGas(evmGasLeft, 375) + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 4) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 750) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } + ip := add(ip, 1) + } + case 0xA3 { // OP_LOG3 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 5) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1125) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) + } + ip := add(ip, 1) + } + case 0xA4 { // OP_LOG4 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 6) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1500) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3, topic4 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + topic4, sp := popStackItemWithoutCheck(sp) + log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) + } + ip := add(ip, 1) + } + case 0xF0 { // OP_CREATE + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) + ip := add(ip, 1) + } + case 0xF1 { // OP_CALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + + // A function was implemented in order to avoid stack depth errors. + gasUsed, sp := performCall(sp, evmGasLeft, isStatic) + + // Check if the following is ok + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF3 { // OP_RETURN + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset,size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET_INNER(), offset) + break + } + case 0xF4 { // OP_DELEGATECALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + sp, isStatic, gasUsed := delegateCall(sp, isStatic, evmGasLeft) + + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF5 { // OP_CREATE2 + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } + ip := add(ip, 1) + } + case 0xFA { // OP_STATICCALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + gasUsed, sp := performStaticCall(sp,evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,gasUsed) + ip := add(ip, 1) + } + case 0xFD { // OP_REVERT + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET_INNER()) + offset,size := addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) + + revert(offset,size) + } + case 0xFE { // OP_INVALID + evmGasLeft := 0 + + revertWithGas(evmGasLeft) + } + default { + printString("INVALID OPCODE") + printHex(opcode) + revert(0, 0) + } + } + + + retGasLeft := evmGasLeft + } + + //////////////////////////////////////////////////////////////// + // FALLBACK + //////////////////////////////////////////////////////////////// + + let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() + + if isStatic { + revert(0, 0) + } + + getConstructorBytecode() + + if iszero(isCallerEVM) { + evmGasLeft := getEVMGas() + } + + let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) + + gasToReturn := validateCorrectBytecode(offset, len, gasToReturn) + + offset, len := padBytecode(offset, len) + + setDeployedCode(gasToReturn, offset, len) + } + object "EVMInterpreter_deployed" { + code { + function ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008002 + } + + function NONCE_HOLDER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008003 + } + + function DEPLOYER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008006 + } + + function CODE_ORACLE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008012 + } + + function EVM_GAS_MANAGER_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008013 + } + + function DEBUG_SLOT_OFFSET() -> offset { + offset := mul(32, 32) // TODO cleanup + } + + function LAST_RETURNDATA_SIZE_OFFSET() -> offset { + offset := add(DEBUG_SLOT_OFFSET(), mul(5, 32)) + } + + function STACK_OFFSET() -> offset { + offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 32) + } + + function BYTECODE_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1024, 32)) + } + + function INF_PASS_GAS() -> inf { + inf := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + } + + function MAX_POSSIBLE_BYTECODE() -> max { + max := 32000 + } + + function MEM_OFFSET() -> offset { + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) + } + + function MEM_OFFSET_INNER() -> offset { + offset := add(MEM_OFFSET(), 32) + } + + function MAX_POSSIBLE_MEM() -> max { + max := 0x100000 // 1MB + } + + function MAX_MEMORY_FRAME() -> max { + max := add(MEM_OFFSET_INNER(), MAX_POSSIBLE_MEM()) + } + + function MAX_UINT() -> max_uint { + max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + } + + // Essentially a NOP that will not get optimized away by the compiler + function $llvm_NoInline_llvm$_unoptimized() { + pop(1) + } + + function printHex(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebde) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() + } + + function printString(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdf) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() + } + + // It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 + function readIP(ip,maxAcceptablePos) -> opcode { + if gt(ip, maxAcceptablePos) { + revert(0, 0) + } + + opcode := and(mload(sub(ip, 31)), 0xff) + } + + function readBytes(start, maxAcceptablePos,length) -> value { + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) + } + value := shr(mul(8,sub(32,length)),mload(start)) + } + + function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, sub(position, 1))) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + let dup := mload(tempSp) + + newSp := add(sp, 0x20) + mstore(newSp, dup) + } + + function swapStackItem(sp, evmGas, position) -> evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, position)) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + + let s2 := mload(sp) + let s1 := mload(tempSp) + + mstore(sp, s1) + mstore(tempSp, s2) + } + + function popStackItem(sp, evmGasLeft) -> a, newSp { + // We can not return any error here, because it would break compatibility + if lt(sp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + a := mload(sp) + newSp := sub(sp, 0x20) + } + + function pushStackItem(sp, item, evmGasLeft) -> newSp { + if or(gt(sp, BYTECODE_OFFSET()), eq(sp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + newSp := add(sp, 0x20) + mstore(newSp, item) + } + + function popStackItemWithoutCheck(sp) -> a, newSp { + a := mload(sp) + newSp := sub(sp, 0x20) + } + + function pushStackItemWithoutCheck(sp, item) -> newSp { + newSp := add(sp, 0x20) + mstore(newSp, item) + } + + function popStackCheck(sp, evmGasLeft, numInputs) { + if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + } + + function pushStackCheck(sp, evmGasLeft, numInputs) { + if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + } + + function getCodeAddress() -> addr { + addr := verbatim_0i_1o("code_source") + } + + function loadReturndataIntoActivePtr() { + verbatim_0i_0o("return_data_ptr_to_active") + } + + function loadCalldataIntoActivePtr() { + verbatim_0i_0o("calldata_ptr_to_active") + } + + function getActivePtrDataSize() -> size { + size := verbatim_0i_1o("active_ptr_data_size") + } + + function copyActivePtrData(_dest, _source, _size) { + verbatim_3i_0o("active_ptr_data_copy", _dest, _source, _size) + } + + function ptrAddIntoActive(_dest) { + verbatim_1i_0o("active_ptr_add_assign", _dest) + } + + function ptrShrinkIntoActive(_dest) { + verbatim_1i_0o("active_ptr_shrink_assign", _dest) + } + + function _getRawCodeHash(account) -> hash { + mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) + mstore(4, account) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + hash := mload(0) + } + + function getIsStaticFromCallFlags() -> isStatic { + isStatic := verbatim_0i_1o("get_global::call_flags") + isStatic := iszero(iszero(and(isStatic, 0x04))) + } + + // Basically performs an extcodecopy, while returning the length of the bytecode. + function _fetchDeployedCode(addr, _offset, _len) -> codeLen { + codeLen := _fetchDeployedCodeWithDest(addr, 0, _len, _offset) + } + + // Basically performs an extcodecopy, while returning the length of the bytecode. + function _fetchDeployedCodeWithDest(addr, _offset, _len, dest) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + + if gt(_len, codeLen) { + _len := codeLen + } + + returndatacopy(dest, add(32,_offset), _len) + } + + // Returns the length of the bytecode. + function _fetchDeployedCodeLen(addr) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + switch iszero(success) + case 1 { + // The code oracle call can only fail in the case where the contract + // we are querying is the current one executing and it has not yet been + // deployed, i.e., if someone calls codesize (or extcodesize(address())) + // inside the constructor. In that case, code length is zero. + codeLen := 0 + } + default { + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + } + } + + function getDeployedBytecode() { + let codeLen := _fetchDeployedCode( + getCodeAddress(), + add(BYTECODE_OFFSET(), 32), + MAX_POSSIBLE_BYTECODE() + ) + + mstore(BYTECODE_OFFSET(), codeLen) + } + + function consumeEvmFrame() -> passGas, isStatic, callerEVM { + // function consumeEvmFrame() external returns (uint256 passGas, bool isStatic) + mstore(0, 0x04C14E9E00000000000000000000000000000000000000000000000000000000) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // Should never happen + revert(0, 0) + } + + returndatacopy(0,0,64) + + passGas := mload(0) + isStatic := mload(32) + + if iszero(eq(passGas, INF_PASS_GAS())) { + callerEVM := true + } + } + + function chargeGas(prevGas, toCharge) -> gasRemaining { + if lt(prevGas, toCharge) { + revertWithGas(0) + } + + gasRemaining := sub(prevGas, toCharge) + } + + function getMax(a, b) -> max { + max := b + if gt(a, b) { + max := a + } + } + + function bitLength(n) -> bitLen { + for { } gt(n, 0) { } { // while(n > 0) + if iszero(n) { + bitLen := 1 + break + } + n := shr(1, n) + bitLen := add(bitLen, 1) + } + } + + function bitMaskFromBytes(nBytes) -> bitMask { + bitMask := sub(exp(2, mul(nBytes, 8)), 1) // 2**(nBytes*8) - 1 + } + // The gas cost mentioned here is purely the cost of the contract, + // and does not consider the cost of the call itself nor the instructions + // to put the parameters in memory. + // Take into account MEM_OFFSET_INNER() when passing the argsOffset + function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { + switch addr + case 0x01 { // ecRecover + gasToCharge := 3000 + } + case 0x02 { // SHA2-256 + gasToCharge := 60 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(12, dataWordSize)) + } + case 0x03 { // RIPEMD-160 + gasToCharge := 600 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(120, dataWordSize)) + } + case 0x04 { // identity + gasToCharge := 15 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(3, dataWordSize)) + } + // [0; 31] (32 bytes) Bsize Byte size of B + // [32; 63] (32 bytes) Esize Byte size of E + // [64; 95] (32 bytes) Msize Byte size of M + /* + def calculate_iteration_count(exponent_length, exponent): + iteration_count = 0 + if exponent_length <= 32 and exponent == 0: iteration_count = 0 + elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + return max(iteration_count, 1) + def calculate_gas_cost(base_length, modulus_length, exponent_length, exponent): + multiplication_complexity = calculate_multiplication_complexity(base_length, modulus_length) + iteration_count = calculate_iteration_count(exponent_length, exponent) + return max(200, math.floor(multiplication_complexity * iteration_count / 3)) + */ + // modexp gas cost EIP below + // https://eips.ethereum.org/EIPS/eip-2565 + case 0x05 { // modexp + let mulComplex + let Bsize := mload(argsOffset) + let Esize := mload(add(argsOffset, 0x20)) + + { + let words := getMax(Bsize, mload(add(argsOffset, 0x40))) // shr(3, x) == x/8 + if and(lt(words, 64), eq(words, 64)){ + // if x <= 64: return x ** 2 + mulComplex := mul(words, words) + } + if and(and(lt(words, 1024), eq(words, 1024)), gt(words, 64)){ + // elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 + mulComplex := sub(add(shr(2, mul(words, words)), mul(96, words)), 3072) + } + if gt(words, 64) { + // else: return x ** 2 // 16 + 480 * x - 199680 + mulComplex := sub(add(shr(4, mul(words, words)), mul(480, words)), 199680) + } + } + + // [96 + Bsize; 96 + Bsize + Esize] E + let exponentFirst256, exponentIsZero, exponentBitLen + if or(lt(Esize, 32), eq(Esize, 32)) { + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentBitLen := bitLength(exponentFirst256) + exponentIsZero := iszero(and(exponentFirst256, bitMaskFromBytes(Esize))) + } + if gt(Esize, 32) { + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentIsZero := iszero(exponentFirst256) + let exponentNext + // This is done because the first 32bytes of the exponent were loaded + for { let i := 0 } lt(i, div(Esize, 32)) { i := add(i, 1) Esize := sub(Esize, 32) } { // check every 32bytes + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentNext := mload(add(add(add(argsOffset, 0x60), Bsize), add(mul(i, 32), 32))) + exponentBitLen := add(bitLength(exponentNext), mul(mul(32, 8), add(i, 1))) + if iszero(iszero(and(exponentNext, bitMaskFromBytes(Esize)))) { + exponentIsZero := false + } + } + } + + // if exponent_length <= 32 and exponent == 0: iteration_count = 0 + // return max(iteration_count, 1) + let iterationCount := 1 + // elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + if and(lt(Esize, 32), iszero(exponentIsZero)) { + iterationCount := sub(exponentBitLen, 1) + } + // elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + if gt(Esize, 32) { + iterationCount := add(mul(8, sub(Esize, 32)), sub(bitLength(and(exponentFirst256, MAX_UINT())), 1)) + } + + gasToCharge := getMax(200, div(mul(mulComplex, iterationCount), 3)) + } + // ecAdd ecMul ecPairing EIP below + // https://eips.ethereum.org/EIPS/eip-1108 + case 0x06 { // ecAdd + // The gas cost is fixed at 150. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 150 + } + case 0x07 { // ecMul + // The gas cost is fixed at 6000. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 6000 + } + // 35,000 * k + 45,000 gas, where k is the number of pairings being computed. + // The input must always be a multiple of 6 32-byte values. + case 0x08 { // ecPairing + gasToCharge := 45000 + let k := div(argsSize, 0xC0) // 0xC0 == 6*32 + gasToCharge := add(gasToCharge, mul(k, 35000)) + } + case 0x09 { // blake2f + // argsOffset[0; 3] (4 bytes) Number of rounds (big-endian uint) + gasToCharge := and(mload(argsOffset), 0xFFFFFFFF) // last 4bytes + } + default { + gasToCharge := 0 + } + } + + function checkMemOverflowByOffset(offset, evmGasLeft) { + if gt(offset, MAX_POSSIBLE_MEM()) { + mstore(0, evmGasLeft) + revert(0, 32) + } + } + + function checkMemOverflow(location, evmGasLeft) { + if gt(location, MAX_MEMORY_FRAME()) { + mstore(0, evmGasLeft) + revert(0, 32) + } + } + + function checkOverflow(data1, data2, evmGasLeft) { + if lt(add(data1, data2), data2) { + revertWithGas(evmGasLeft) + } + } + + function revertWithGas(evmGasLeft) { + mstore(0, evmGasLeft) + revert(0, 32) + } + + // This function can overflow, it is the job of the caller to ensure that it does not. + // The argument to this function is the offset into the memory region IN BYTES. + function expandMemory(newSize) -> gasCost { + let oldSizeInWords := mload(MEM_OFFSET()) + + // The add 31 here before dividing is there to account for misaligned + // memory expansions, where someone calls this with a newSize that is not + // a multiple of 32. For instance, if someone calls it with an offset of 33, + // the new size in words should be 2, not 1, but dividing by 32 will give 1. + // Adding 31 solves it. + let newSizeInWords := div(add(newSize, 31), 32) + + if gt(newSizeInWords, oldSizeInWords) { + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) + + mstore(MEM_OFFSET(), newSizeInWords) + } + } + + function isSlotWarm(key) -> isWarm { + mstore(0, 0x482D2E7400000000000000000000000000000000000000000000000000000000) + mstore(4, key) + + let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isWarm := mload(0) + } + + function warmSlot(key,currentValue) -> isWarm, originalValue { + mstore(0, 0xBDF7816000000000000000000000000000000000000000000000000000000000) + mstore(4, key) + mstore(36,currentValue) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 64) + + isWarm := mload(0) + originalValue := mload(32) + } + + function getFarCallABI( + dataOffset, + memoryPage, + dataStart, + dataLength, + gasPassed, + shardId, + forwardingMode, + isConstructorCall, + isSystemCall + ) -> ret { + let farCallAbi := 0 + farCallAbi := or(farCallAbi, dataOffset) + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gasPassed)) + farCallAbi := or(farCallAbi, shl(224, shardId)) + farCallAbi := or(farCallAbi, shl(232, forwardingMode)) + farCallAbi := or(farCallAbi, shl(248, 1)) + ret := farCallAbi + } + + function addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) -> newOffset,newSize { + newOffset := offset + newSize := size + if eq(isCallerEVM,1) { + // include gas + let previousValue := mload(sub(offset,32)) + mstore(sub(offset,32),evmGasLeft) + //mstore(sub(offset,32),previousValue) // Im not sure why this is needed, it was like this in the solidity code, + // but it appears to rewrite were we want to store the gas + + newOffset := sub(offset, 32) + newSize := add(size, 32) + } + } + + function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { + mstore(0, 0x8DB2BA7800000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 36, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 32) + isWarm := mload(0) + } + + function getRawNonce(addr) -> nonce { + mstore(0, 0x5AA9B6B500000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) + } + + function _isEVM(_addr) -> isEVM { + // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) + // function isAccountEVM(address _addr) external view returns (bool); + // IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( + // address(SYSTEM_CONTRACTS_OFFSET + 0x02) + // ); + + mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) + mstore(4, _addr) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isEVM := mload(0) + } + + function _pushEVMFrame(_passGas, _isStatic) { + // function pushEVMFrame(uint256 _passGas, bool _isStatic) external + + mstore(0, 0xEAD7715600000000000000000000000000000000000000000000000000000000) + mstore(4, _passGas) + mstore(36, _isStatic) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function _popEVMFrame() { + // function popEVMFrame() external + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + + mstore(0, 0xE467D2F000000000000000000000000000000000000000000000000000000000) + + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + // Each evm gas is 5 zkEVM one + function GAS_DIVISOR() -> gas_div { gas_div := 5 } + function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 + function OVERHEAD() -> overhead { overhead := 2000 } + + // From precompiles/CodeOracle + function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } + function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + + function _calcEVMGas(_zkevmGas) -> calczkevmGas { + calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) + } + + function getEVMGas() -> evmGas { + let _gas := gas() + let requiredGas := add(EVM_GAS_STIPEND(), OVERHEAD()) + + switch lt(_gas, requiredGas) + case 1 { + evmGas := 0 + } + default { + evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) + } + } + + function _getZkEVMGas(_evmGas, addr) -> zkevmGas { + zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } + } + + function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + let rtsz := returndatasize() + + loadReturndataIntoActivePtr() + + // if (rtsz > 31) + switch gt(rtsz, 31) + case 0 { + // Unexpected return data. + _gasLeft := 0 + _eraseReturndataPointer() + } + default { + returndatacopy(0, 0, 32) + _gasLeft := mload(0) + + // We copy as much returndata as possible without going over the + // returndata size. + switch lt(sub(rtsz, 32), _outputLen) + case 0 { returndatacopy(_outputOffset, 32, _outputLen) } + default { returndatacopy(_outputOffset, 32, sub(rtsz, 32)) } + + mstore(lastRtSzOffset, sub(rtsz, 32)) + + // Skip the returnData + ptrAddIntoActive(32) + } + } + + function _eraseReturndataPointer() { + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + let activePtrSize := getActivePtrDataSize() + ptrShrinkIntoActive(and(activePtrSize, 0xFFFFFFFF))// uint32(activePtrSize) + mstore(lastRtSzOffset, 0) + } + + function _saveReturndataAfterZkEVMCall() { + loadReturndataIntoActivePtr() + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + mstore(lastRtSzOffset, returndatasize()) + } + + function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp { + let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + + popStackCheck(oldSp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + if gt(gasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } + + let frameGasLeft + let success + switch _isEVM(addr) + case 0 { + // zkEVM native + gasToPass := _getZkEVMGas(gasToPass, addr) + let zkevmGasBefore := gas() + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPass, gasUsed) { + frameGasLeft := sub(gasToPass, gasUsed) + } + } + default { + _pushEVMFrame(gasToPass, true) + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, 0, 0) + + frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + _popEVMFrame() + } + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + + sp := pushStackItem(sp, success, evmGasLeft) + } + function capGas(evmGasLeft,oldGasToPass) -> gasToPass { + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + gasToPass := oldGasToPass + if gt(oldGasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } + } + + function getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) -> maxExpand{ + maxExpand := add(retOffset, retSize) + switch lt(maxExpand,add(argsOffset, argsSize)) + case 0 { + maxExpand := expandMemory(maxExpand) + } + default { + maxExpand := expandMemory(add(argsOffset, argsSize)) + } + } + + function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + + switch isStatic + case 0 { + switch is_evm + case 0 { + // zkEVM native + gasToPassNew := _getZkEVMGas(gasToPassNew, addr) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } + default { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(EVM_GAS_STIPEND(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + } + default { + if value { + revertWithGas(gasToPassNew) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } + } + + function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { + let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize + + popStackCheck(oldSp, evmGasLeft, 7) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + // static_gas = 0 + // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost + // code_execution_cost is the cost of the called code execution (limited by the gas parameter). + // If address is warm, then address_access_cost is 100, otherwise it is 2600. See section access sets. + // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. 2300 is thus removed from the cost, and also added to the gas input. + // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + if gt(value, 0) { + extraCost := add(extraCost,6700) + gasToPass := add(gasToPass,2300) + } + + if and(isAddrEmpty(addr), gt(value, 0)) { + extraCost := add(extraCost,25000) + } + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + argsOffset := add(argsOffset,MEM_OFFSET_INNER()) + retOffset := add(retOffset,MEM_OFFSET_INNER()) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset,retSize, evmGasLeft) + + checkMemOverflow(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflow(add(retOffset, retSize), evmGasLeft) + + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp,success, evmGasLeft) + } + + function delegateCall(oldSp, oldIsStatic, evmGasLeft) -> sp, isStatic, extraCost { + let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + + sp := oldSp + isStatic := oldIsStatic + + popStackCheck(sp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(sp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset, argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + if iszero(_isEVM(addr)) { + revertWithGas(evmGasLeft) + } + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + _pushEVMFrame(gasToPass, isStatic) + let success := delegatecall( + // We can not just pass all gas here to prevent overflow of zkEVM gas counter + EVM_GAS_STIPEND(), + addr, + add(MEM_OFFSET_INNER(), argsOffset), + argsSize, + 0, + 0 + ) + + let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + + _popEVMFrame() + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp, success, evmGasLeft) + } + + function _performStaticCall( + _calleeIsEVM, + _calleeGas, + _callee, + _inputOffset, + _inputLen, + _outputOffset, + _outputLen + ) -> success, _gasLeft { + switch _calleeIsEVM + case 0 { + // zkEVM native + _calleeGas := _getZkEVMGas(_calleeGas, _callee) + let zkevmGasBefore := gas() + success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) + + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + _gasLeft := 0 + if gt(_calleeGas, gasUsed) { + _gasLeft := sub(_calleeGas, gasUsed) + } + } + default { + _pushEVMFrame(_calleeGas, true) + success := staticcall(EVM_GAS_STIPEND(), _callee, _inputOffset, _inputLen, 0, 0) + + _gasLeft := _saveReturndataAfterEVMCall(_outputOffset, _outputLen) + _popEVMFrame() + } + } + + function isAddrEmpty(addr) -> isEmpty { + isEmpty := 0 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } + } + } + + function _fetchConstructorReturnGas() -> gasLeft { + mstore(0, 0x24E5AB4A00000000000000000000000000000000000000000000000000000000) + + let success := staticcall(gas(), DEPLOYER_SYSTEM_CONTRACT(), 0, 4, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + gasLeft := mload(0) + } + + function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt) -> result, evmGasLeft, addr { + pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) + + _eraseReturndataPointer() + + let gasForTheCall := capGas(evmGasLeftOld,INF_PASS_GAS()) + + if lt(selfbalance(),value) { + revertWithGas(evmGasLeftOld) + } + + offset := add(MEM_OFFSET_INNER(), offset) + + pushStackCheck(sp, evmGasLeftOld, 4) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20))) + + _pushEVMFrame(gasForTheCall, false) + + if isCreate2 { + // Create2EVM selector + mstore(sub(offset, 0x80), 0x4e96f4c0) + // salt + mstore(sub(offset, 0x60), salt) + // Where the arg starts (third word) + mstore(sub(offset, 0x40), 0x40) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x64), add(size, 0x64), 0, 32) + } + + + if iszero(isCreate2) { + // CreateEVM selector + mstore(sub(offset, 0x60), 0xff311601) + // Where the arg starts (second word) + mstore(sub(offset, 0x40), 0x20) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x44), add(size, 0x44), 0, 32) + } + + addr := mload(0) + + let gasLeft + switch result + case 0 { + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) + + _popEVMFrame() + + let back + + // skipping check since we pushed exactly 4 items earlier + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x20), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x40), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x60), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x80), back) + } + + function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { + let rest_bits := shl(3, len) + let upper_bits := sub(256, rest_bits) + let val_mask := shl(upper_bits, MAX_UINT()) + let val_masked := and(val, val_mask) + let dst_val := mload(dest) + let dst_mask := shr(rest_bits, MAX_UINT()) + let dst_masked := and(dst_val, dst_mask) + mstore(dest, or(val_masked, dst_masked)) + } + + function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { + let dest_addr := dest + let src_addr := src + let dest_end := add(dest, and(len, sub(0, 32))) + for { } lt(dest_addr, dest_end) {} { + mstore(dest_addr, mload(src_addr)) + dest_addr := add(dest_addr, 32) + src_addr := add(src_addr, 32) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + } + } + + function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { + let dest_end := add(dest, and(len, sub(0, 32))) + for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { + mstore(i, 0) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + } + } + + function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + popStackCheck(oldSp, evmGasLeft, 4) + addr, sp := popStackItemWithoutCheck(oldSp) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) + + // Gets the code from the addr + if and(iszero(iszero(_getRawCodeHash(addr))),gt(len,0)) { + pop(_fetchDeployedCodeWithDest(addr, offset, len,add(dest,MEM_OFFSET_INNER()))) + } + } + + function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size + + popStackCheck(oldSp, evmGasLeft, 3) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + let dynamicGas := add( + shr(4, add(size, 31)), + expandMemory(add(offset, size)) + ) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let result, addr + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0) + + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } + } + + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size, salt + + popStackCheck(oldSp, evmGasLeft, 4) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + salt, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // hash_cost = 6 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + evmGasLeft := chargeGas(evmGasLeft, add( + expandMemory(add(offset, size)), + shr(2, add(size, 31)) + )) + + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft,true,salt) + } + + + function $llvm_NoInline_llvm$_simulate( + isCallerEVM, + evmGasLeft, + isStatic, + ) -> returnOffset, returnLen { + + returnOffset := MEM_OFFSET_INNER() + returnLen := 0 + + // stack pointer - index to first stack element; empty stack = -1 + let sp := sub(STACK_OFFSET(), 32) + // instruction pointer - index to next instruction. Not called pc because it's an + // actual yul/evm instruction. + let ip := add(BYTECODE_OFFSET(), 32) + let opcode + + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + + for { } true { } { + opcode := readIP(ip,maxAcceptablePos) + + switch opcode + case 0x00 { // OP_STOP + break + } + case 0x01 { // OP_ADD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, add(a, b)) + ip := add(ip, 1) + } + case 0x02 { // OP_MUL + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mul(a, b)) + ip := add(ip, 1) + } + case 0x03 { // OP_SUB + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sub(a, b)) + ip := add(ip, 1) + } + case 0x04 { // OP_DIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, div(a, b)) + ip := add(ip, 1) + } + case 0x05 { // OP_SDIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sdiv(a, b)) + ip := add(ip, 1) + } + case 0x06 { // OP_MOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mod(a, b)) + ip := add(ip, 1) + } + case 0x07 { // OP_SMOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, smod(a, b)) + ip := add(ip, 1) + } + case 0x08 { // OP_ADDMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, addmod(a, b, N)) + ip := add(ip, 1) + } + case 0x09 { // OP_MULMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItem(sp, mulmod(a, b, N), evmGasLeft) + ip := add(ip, 1) + } + case 0x0A { // OP_EXP + evmGasLeft := chargeGas(evmGasLeft, 10) + + let a, exponent + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + exponent, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, exp(a, exponent)) + + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) + ip := add(ip, 1) + } + case 0x0B { // OP_SIGNEXTEND + evmGasLeft := chargeGas(evmGasLeft, 5) + + let b, x + + popStackCheck(sp, evmGasLeft, 2) + b, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, signextend(b, x)) + ip := add(ip, 1) + } + case 0x10 { // OP_LT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, lt(a, b)) + ip := add(ip, 1) + } + case 0x11 { // OP_GT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, gt(a, b)) + ip := add(ip, 1) + } + case 0x12 { // OP_SLT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, slt(a, b)) + ip := add(ip, 1) + } + case 0x13 { // OP_SGT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sgt(a, b)) + ip := add(ip, 1) + } + case 0x14 { // OP_EQ + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, eq(a, b)) + ip := add(ip, 1) + } + case 0x15 { // OP_ISZERO + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, iszero(a)) + ip := add(ip, 1) + } + case 0x16 { // OP_AND + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, and(a,b)) + ip := add(ip, 1) + } + case 0x17 { // OP_OR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, or(a,b)) + ip := add(ip, 1) + } + case 0x18 { // OP_XOR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, xor(a, b)) + ip := add(ip, 1) + } + case 0x19 { // OP_NOT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, not(a)) + ip := add(ip, 1) + } + case 0x1A { // OP_BYTE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i, x + + popStackCheck(sp, evmGasLeft, 2) + i, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, byte(i, x)) + ip := add(ip, 1) + } + case 0x1B { // OP_SHL + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shl(shift, value)) + ip := add(ip, 1) + } + case 0x1C { // OP_SHR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shr(shift, value)) + ip := add(ip, 1) + } + case 0x1D { // OP_SAR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sar(shift, value)) + ip := add(ip, 1) + } + case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) + + let offset, size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + let keccak := keccak256(add(MEM_OFFSET_INNER(), offset), size) + + // When an offset is first accessed (either read or write), memory may trigger + // an expansion, which costs gas. + // dynamicGas = 6 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + sp := pushStackItem(sp, keccak, evmGasLeft) + ip := add(ip, 1) + } + case 0x30 { // OP_ADDRESS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, address(), evmGasLeft) + ip := add(ip, 1) + } + case 0x31 { // OP_BALANCE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + sp := pushStackItemWithoutCheck(sp, balance(addr)) + ip := add(ip, 1) + } + case 0x32 { // OP_ORIGIN + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, origin(), evmGasLeft) + ip := add(ip, 1) + } + case 0x33 { // OP_CALLER + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, caller(), evmGasLeft) + ip := add(ip, 1) + } + case 0x34 { // OP_CALLVALUE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, callvalue(), evmGasLeft) + ip := add(ip, 1) + } + case 0x35 { // OP_CALLDATALOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i + + popStackCheck(sp, evmGasLeft, 1) + i, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, calldataload(i)) + ip := add(ip, 1) + } + case 0x36 { // OP_CALLDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, calldatasize(), evmGasLeft) + ip := add(ip, 1) + } + case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let destOffset, offset, size + + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(destOffset,size), evmGasLeft) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + ip := add(ip, 1) + + } + case 0x38 { // OP_CODESIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let bytecodeLen := mload(BYTECODE_OFFSET()) + sp := pushStackItem(sp, bytecodeLen, evmGasLeft) + ip := add(ip, 1) + } + case 0x39 { // OP_CODECOPY + + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dst, offset, len + + popStackCheck(sp, evmGasLeft, 3) + dst, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + dst := add(dst, MEM_OFFSET_INNER()) + offset := add(add(offset, BYTECODE_OFFSET()), 32) + + checkOverflow(dst,len, evmGasLeft) + checkOverflow(offset,len, evmGasLeft) + checkMemOverflow(add(dst, len), evmGasLeft) + // Check bytecode overflow + if gt(add(offset, len), sub(MEM_OFFSET(), 1)) { + revertWithGas(evmGasLeft) + } + + $llvm_AlwaysInline_llvm$_memcpy(dst, offset, len) + ip := add(ip, 1) + } + case 0x3A { // OP_GASPRICE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, gasprice(), evmGasLeft) + ip := add(ip, 1) + } + case 0x3B { // OP_EXTCODESIZE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + switch _isEVM(addr) + case 0 { sp := pushStackItemWithoutCheck(sp, extcodesize(addr)) } + default { sp := pushStackItemWithoutCheck(sp, _fetchDeployedCodeLen(addr)) } + ip := add(ip, 1) + } + case 0x3C { // OP_EXTCODECOPY + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) + ip := add(ip, 1) + } + case 0x3D { // OP_RETURNDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let rdz := mload(LAST_RETURNDATA_SIZE_OFFSET()) + sp := pushStackItem(sp, rdz, evmGasLeft) + ip := add(ip, 1) + } + case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dest, offset, len + popStackCheck(sp, evmGasLeft, 3) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,len, evmGasLeft) + if gt(add(offset, len), mload(LAST_RETURNDATA_SIZE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + // minimum_word_size = (size + 31) / 32 + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + checkMemOverflowByOffset(offset, evmGasLeft) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + ip := add(ip, 1) + } + case 0x3F { // OP_EXTCODEHASH + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + ip := add(ip, 1) + if iszero(addr) { + sp := pushStackItemWithoutCheck(sp, 0) + continue + } + sp := pushStackItemWithoutCheck(sp, extcodehash(addr)) + } + case 0x40 { // OP_BLOCKHASH + evmGasLeft := chargeGas(evmGasLeft, 20) + + let blockNumber + popStackCheck(sp, evmGasLeft, 1) + blockNumber, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, blockhash(blockNumber)) + ip := add(ip, 1) + } + case 0x41 { // OP_COINBASE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, coinbase(), evmGasLeft) + ip := add(ip, 1) + } + case 0x42 { // OP_TIMESTAMP + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, timestamp(), evmGasLeft) + ip := add(ip, 1) + } + case 0x43 { // OP_NUMBER + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, number(), evmGasLeft) + ip := add(ip, 1) + } + case 0x44 { // OP_PREVRANDAO + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, prevrandao(), evmGasLeft) + ip := add(ip, 1) + } + case 0x45 { // OP_GASLIMIT + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, gaslimit(), evmGasLeft) + ip := add(ip, 1) + } + case 0x46 { // OP_CHAINID + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, chainid(), evmGasLeft) + ip := add(ip, 1) + } + case 0x47 { // OP_SELFBALANCE + evmGasLeft := chargeGas(evmGasLeft, 5) + sp := pushStackItem(sp, selfbalance(), evmGasLeft) + ip := add(ip, 1) + } + case 0x48 { // OP_BASEFEE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, basefee(), evmGasLeft) + ip := add(ip, 1) + } + case 0x50 { // OP_POP + evmGasLeft := chargeGas(evmGasLeft, 2) + + let _y + + _y, sp := popStackItem(sp, evmGasLeft) + ip := add(ip, 1) + } + case 0x51 { // OP_MLOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset + + offset, sp := popStackItem(sp, evmGasLeft) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + let memValue := mload(add(MEM_OFFSET_INNER(), offset)) + sp := pushStackItemWithoutCheck(sp, memValue) + ip := add(ip, 1) + } + case 0x52 { // OP_MSTORE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x53 { // OP_MSTORE8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 1)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore8(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x54 { // OP_SLOAD + + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key, value, isWarm + + key, sp := popStackItem(sp, evmGasLeft) + + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { + evmGasLeft := chargeGas(evmGasLeft, 2000) + } + + value := sload(key) + + if iszero(wasWarm) { + let _wasW, _orgV := warmSlot(key, value) + } + + sp := pushStackItemWithoutCheck(sp,value) + ip := add(ip, 1) + } + case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value, gasSpent + + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + ip := add(ip, 1) + { + // Here it is okay to read before we charge since we known anyway that + // the context has enough funds to compensate at least for the read. + // Im not sure if we need this before: require(gasLeft > GAS_CALL_STIPEND); + let currentValue := sload(key) + let wasWarm, originalValue := warmSlot(key, currentValue) + + if eq(value, currentValue) { + continue + } + + if eq(originalValue, currentValue) { + gasSpent := 19900 + if originalValue { + gasSpent := 2800 + } + } + + if iszero(wasWarm) { + gasSpent := add(gasSpent, 2100) + } + } + + evmGasLeft := chargeGas(evmGasLeft, gasSpent) + sstore(key, value) + + } + // NOTE: We don't currently do full jumpdest validation + // (i.e. validating a jumpdest isn't in PUSH data) + case 0x56 { // OP_JUMP + evmGasLeft := chargeGas(evmGasLeft, 8) + + let counter + + counter, sp := popStackItem(sp, evmGasLeft) + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x57 { // OP_JUMPI + evmGasLeft := chargeGas(evmGasLeft, 10) + + let counter, b + + popStackCheck(sp, evmGasLeft, 2) + counter, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + if iszero(b) { + ip := add(ip, 1) + continue + } + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x58 { // OP_PC + evmGasLeft := chargeGas(evmGasLeft, 2) + ip := add(ip, 1) + + // PC = ip - 32 (bytecode size) - 1 (current instruction) + sp := pushStackItem(sp, sub(sub(ip, BYTECODE_OFFSET()), 33), evmGasLeft) + } + case 0x59 { // OP_MSIZE + evmGasLeft := chargeGas(evmGasLeft,2) + + let size + + size := mload(MEM_OFFSET()) + size := shl(5,size) + sp := pushStackItem(sp,size, evmGasLeft) + ip := add(ip, 1) + } + case 0x5A { // OP_GAS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, evmGasLeft, evmGasLeft) + ip := add(ip, 1) + } + case 0x5B { // OP_JUMPDEST + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x5C { // OP_TLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key + popStackCheck(sp, evmGasLeft, 1) + key, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, tload(key)) + ip := add(ip, 1) + } + case 0x5D { // OP_TSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + tstore(key, value) + ip := add(ip, 1) + } + case 0x5E { // OP_MCOPY + let destOffset, offset, size + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + checkMemOverflowByOffset(add(destOffset, size), evmGasLeft) + + expandMemory(add(destOffset, size)) + expandMemory(add(offset, size)) + + mcopy(add(destOffset, MEM_OFFSET_INNER()), add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0x5F { // OP_PUSH0 + evmGasLeft := chargeGas(evmGasLeft, 2) + + let value := 0 + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x60 { // OP_PUSH1 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,1) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x61 { // OP_PUSH2 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,2) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 2) + } + case 0x62 { // OP_PUSH3 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,3) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 3) + } + case 0x63 { // OP_PUSH4 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,4) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 4) + } + case 0x64 { // OP_PUSH5 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,5) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 5) + } + case 0x65 { // OP_PUSH6 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,6) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 6) + } + case 0x66 { // OP_PUSH7 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,7) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 7) + } + case 0x67 { // OP_PUSH8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,8) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 8) + } + case 0x68 { // OP_PUSH9 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,9) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 9) + } + case 0x69 { // OP_PUSH10 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,10) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 10) + } + case 0x6A { // OP_PUSH11 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,11) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 11) + } + case 0x6B { // OP_PUSH12 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,12) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 12) + } + case 0x6C { // OP_PUSH13 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,13) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 13) + } + case 0x6D { // OP_PUSH14 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,14) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 14) + } + case 0x6E { // OP_PUSH15 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,15) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 15) + } + case 0x6F { // OP_PUSH16 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,16) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 16) + } + case 0x70 { // OP_PUSH17 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,17) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 17) + } + case 0x71 { // OP_PUSH18 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,18) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 18) + } + case 0x72 { // OP_PUSH19 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,19) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 19) + } + case 0x73 { // OP_PUSH20 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,20) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 20) + } + case 0x74 { // OP_PUSH21 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,21) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 21) + } + case 0x75 { // OP_PUSH22 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,22) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 22) + } + case 0x76 { // OP_PUSH23 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,23) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 23) + } + case 0x77 { // OP_PUSH24 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,24) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 24) + } + case 0x78 { // OP_PUSH25 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,25) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 25) + } + case 0x79 { // OP_PUSH26 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,26) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 26) + } + case 0x7A { // OP_PUSH27 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,27) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 27) + } + case 0x7B { // OP_PUSH28 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,28) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 28) + } + case 0x7C { // OP_PUSH29 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,29) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 29) + } + case 0x7D { // OP_PUSH30 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,30) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 30) + } + case 0x7E { // OP_PUSH31 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,31) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 31) + } + case 0x7F { // OP_PUSH32 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,32) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 32) + } + case 0x80 { // OP_DUP1 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x81 { // OP_DUP2 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x82 { // OP_DUP3 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x83 { // OP_DUP4 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x84 { // OP_DUP5 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x85 { // OP_DUP6 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x86 { // OP_DUP7 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x87 { // OP_DUP8 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x88 { // OP_DUP9 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x89 { // OP_DUP10 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x8A { // OP_DUP11 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x8B { // OP_DUP12 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x8C { // OP_DUP13 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x8D { // OP_DUP14 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x8E { // OP_DUP15 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x8F { // OP_DUP16 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0x90 { // OP_SWAP1 + evmGasLeft := swapStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x91 { // OP_SWAP2 + evmGasLeft := swapStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x92 { // OP_SWAP3 + evmGasLeft := swapStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x93 { // OP_SWAP4 + evmGasLeft := swapStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x94 { // OP_SWAP5 + evmGasLeft := swapStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x95 { // OP_SWAP6 + evmGasLeft := swapStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x96 { // OP_SWAP7 + evmGasLeft := swapStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x97 { // OP_SWAP8 + evmGasLeft := swapStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x98 { // OP_SWAP9 + evmGasLeft := swapStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x99 { // OP_SWAP10 + evmGasLeft := swapStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x9A { // OP_SWAP11 + evmGasLeft := swapStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x9B { // OP_SWAP12 + evmGasLeft := swapStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x9C { // OP_SWAP13 + evmGasLeft := swapStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x9D { // OP_SWAP14 + evmGasLeft := swapStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x9E { // OP_SWAP15 + evmGasLeft := swapStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x9F { // OP_SWAP16 + evmGasLeft := swapStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0xA0 { // OP_LOG0 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + log0(add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0xA1 { // OP_LOG1 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 3) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 375) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1 + topic1, sp := popStackItemWithoutCheck(sp) + log1(add(offset, MEM_OFFSET_INNER()), size, topic1) + } + ip := add(ip, 1) + } + case 0xA2 { // OP_LOG2 + evmGasLeft := chargeGas(evmGasLeft, 375) + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 4) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 750) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } + ip := add(ip, 1) + } + case 0xA3 { // OP_LOG3 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 5) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1125) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) + } + ip := add(ip, 1) + } + case 0xA4 { // OP_LOG4 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 6) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1500) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3, topic4 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + topic4, sp := popStackItemWithoutCheck(sp) + log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) + } + ip := add(ip, 1) + } + case 0xF0 { // OP_CREATE + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) + ip := add(ip, 1) + } + case 0xF1 { // OP_CALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + + // A function was implemented in order to avoid stack depth errors. + gasUsed, sp := performCall(sp, evmGasLeft, isStatic) + + // Check if the following is ok + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF3 { // OP_RETURN + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset,size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET_INNER(), offset) + break + } + case 0xF4 { // OP_DELEGATECALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + sp, isStatic, gasUsed := delegateCall(sp, isStatic, evmGasLeft) + + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF5 { // OP_CREATE2 + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } + ip := add(ip, 1) + } + case 0xFA { // OP_STATICCALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + gasUsed, sp := performStaticCall(sp,evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,gasUsed) + ip := add(ip, 1) + } + case 0xFD { // OP_REVERT + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET_INNER()) + offset,size := addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) + + revert(offset,size) + } + case 0xFE { // OP_INVALID + evmGasLeft := 0 + + revertWithGas(evmGasLeft) + } + default { + printString("INVALID OPCODE") + printHex(opcode) + revert(0, 0) + } + } + + + if eq(isCallerEVM, 1) { + // Includes gas + returnOffset := sub(returnOffset, 32) + checkOverflow(returnLen, 32, evmGasLeft) + returnLen := add(returnLen, 32) + + mstore(returnOffset, evmGasLeft) + } + } + + //////////////////////////////////////////////////////////////// + // FALLBACK + //////////////////////////////////////////////////////////////// + + let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() + + if iszero(isCallerEVM) { + evmGasLeft := getEVMGas() + isStatic := getIsStaticFromCallFlags() + } + + // First, copy the contract's bytecode to be executed into tEdhe `BYTECODE_OFFSET` + // segment of memory. + getDeployedBytecode() + + pop($llvm_AlwaysInline_llvm$_warmAddress(address())) + + let returnOffset, returnLen := $llvm_NoInline_llvm$_simulate(isCallerEVM, evmGasLeft, isStatic) + return(returnOffset, returnLen) + } + } +} diff --git a/system-contracts/contracts/KnownCodesStorage.sol b/system-contracts/contracts/KnownCodesStorage.sol index 31fa04734..378eeecd2 100644 --- a/system-contracts/contracts/KnownCodesStorage.sol +++ b/system-contracts/contracts/KnownCodesStorage.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.24; import {IKnownCodesStorage} from "./interfaces/IKnownCodesStorage.sol"; import {SystemContractBase} from "./abstract/SystemContractBase.sol"; import {Utils} from "./libraries/Utils.sol"; -import {COMPRESSOR_CONTRACT, L1_MESSENGER_CONTRACT} from "./Constants.sol"; +import {COMPRESSOR_CONTRACT, L1_MESSENGER_CONTRACT, DEPLOYER_SYSTEM_CONTRACT} from "./Constants.sol"; import {Unauthorized, MalformedBytecode, BytecodeError} from "./SystemContractErrors.sol"; /** @@ -84,4 +84,24 @@ contract KnownCodesStorage is IKnownCodesStorage, SystemContractBase { revert MalformedBytecode(BytecodeError.NumberOfWords); } } + + function publishEVMBytecode(bytes calldata bytecode) external onlyCallFrom(address(DEPLOYER_SYSTEM_CONTRACT)) { + /* + TODO: ensure that it is properly padded, etc. + To preserve EVM compatibility, we can not emit any events here. + */ + + bytes32 hash = Utils.hashEVMBytecode(bytecode); + + if (getMarker(hash) == 0) { + // ToDO: use efficient call + L1_MESSENGER_CONTRACT.sendToL1(bytecode); + + assembly { + sstore(hash, 1) + } + } + + emit MarkedAsKnown(hash, getMarker(hash) == 0); + } } diff --git a/system-contracts/contracts/interfaces/IAccountCodeStorage.sol b/system-contracts/contracts/interfaces/IAccountCodeStorage.sol index 5183e77f6..7bd24cc75 100644 --- a/system-contracts/contracts/interfaces/IAccountCodeStorage.sol +++ b/system-contracts/contracts/interfaces/IAccountCodeStorage.sol @@ -14,4 +14,6 @@ interface IAccountCodeStorage { function getCodeHash(uint256 _input) external view returns (bytes32 codeHash); function getCodeSize(uint256 _input) external view returns (uint256 codeSize); + + function isAccountEVM(address _addr) external view returns (bool); } diff --git a/system-contracts/contracts/interfaces/IContractDeployer.sol b/system-contracts/contracts/interfaces/IContractDeployer.sol index 6e0bac3dc..f42403d3a 100644 --- a/system-contracts/contracts/interfaces/IContractDeployer.sol +++ b/system-contracts/contracts/interfaces/IContractDeployer.sol @@ -88,4 +88,14 @@ interface IContractDeployer { /// @notice Can be called by an account to update its nonce ordering function updateNonceOrdering(AccountNonceOrdering _nonceOrdering) external; + + function createEVM(bytes calldata _initCode) external payable returns (address newAddress); + + function create2EVM(bytes32 _salt, bytes calldata _initCode) external payable returns (address); + + function evmCodeHash(address) external view returns (bytes32); + + function setDeployedCode(uint256 constructorGasLeft, bytes calldata newDeployedCode) external; + + function constructorReturnGas() external view returns (uint256); } diff --git a/system-contracts/contracts/interfaces/IEvmGasManager.sol b/system-contracts/contracts/interfaces/IEvmGasManager.sol new file mode 100644 index 000000000..63c42ff2f --- /dev/null +++ b/system-contracts/contracts/interfaces/IEvmGasManager.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IEvmGasManager { + function warmAccount(address account) external payable returns (bool wasWarm); + + function isSlotWarm(uint256 _slot) external view returns (bool); + + function warmSlot(uint256 _slot, uint256 _currentValue) external payable returns (bool, uint256); + + function pushEVMFrame(uint256 _passGas, bool _isStatic) external; + + function consumeEvmFrame() external returns (uint256 passGas, bool isStatic); + + function popEVMFrame() external; +} diff --git a/system-contracts/contracts/interfaces/IKnownCodesStorage.sol b/system-contracts/contracts/interfaces/IKnownCodesStorage.sol index 551cfb0d8..630b41f25 100644 --- a/system-contracts/contracts/interfaces/IKnownCodesStorage.sol +++ b/system-contracts/contracts/interfaces/IKnownCodesStorage.sol @@ -16,4 +16,6 @@ interface IKnownCodesStorage { function markBytecodeAsPublished(bytes32 _bytecodeHash) external; function getMarker(bytes32 _hash) external view returns (uint256); + + function publishEVMBytecode(bytes calldata bytecode) external; } diff --git a/system-contracts/contracts/libraries/SystemContractHelper.sol b/system-contracts/contracts/libraries/SystemContractHelper.sol index e8469e308..b5934e96a 100644 --- a/system-contracts/contracts/libraries/SystemContractHelper.sol +++ b/system-contracts/contracts/libraries/SystemContractHelper.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.20; import {MAX_SYSTEM_CONTRACT_ADDRESS} from "../Constants.sol"; +import {Utils} from "./Utils.sol"; -import {CALLFLAGS_CALL_ADDRESS, CODE_ADDRESS_CALL_ADDRESS, EVENT_WRITE_ADDRESS, EVENT_INITIALIZE_ADDRESS, GET_EXTRA_ABI_DATA_ADDRESS, LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS, META_CODE_SHARD_ID_OFFSET, META_CALLER_SHARD_ID_OFFSET, META_SHARD_ID_OFFSET, META_AUX_HEAP_SIZE_OFFSET, META_HEAP_SIZE_OFFSET, META_PUBDATA_PUBLISHED_OFFSET, META_CALL_ADDRESS, PTR_CALLDATA_CALL_ADDRESS, PTR_ADD_INTO_ACTIVE_CALL_ADDRESS, PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS, PTR_PACK_INTO_ACTIVE_CALL_ADDRESS, PRECOMPILE_CALL_ADDRESS, SET_CONTEXT_VALUE_CALL_ADDRESS, TO_L1_CALL_ADDRESS} from "./SystemContractsCaller.sol"; +import {SystemContractsCaller, CalldataForwardingMode, CALLFLAGS_CALL_ADDRESS, CODE_ADDRESS_CALL_ADDRESS, EVENT_WRITE_ADDRESS, EVENT_INITIALIZE_ADDRESS, GET_EXTRA_ABI_DATA_ADDRESS, LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS, META_CODE_SHARD_ID_OFFSET, META_CALLER_SHARD_ID_OFFSET, META_SHARD_ID_OFFSET, META_AUX_HEAP_SIZE_OFFSET, META_HEAP_SIZE_OFFSET, META_PUBDATA_PUBLISHED_OFFSET, META_CALL_ADDRESS, PTR_CALLDATA_CALL_ADDRESS, PTR_ADD_INTO_ACTIVE_CALL_ADDRESS, PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS, PTR_PACK_INTO_ACTIVE_CALL_ADDRESS, PRECOMPILE_CALL_ADDRESS, SET_CONTEXT_VALUE_CALL_ADDRESS, TO_L1_CALL_ADDRESS, MIMIC_CALL_CALL_ADDRESS} from "./SystemContractsCaller.sol"; import {IndexOutOfBounds, FailedToChargeGas} from "../SystemContractErrors.sol"; uint256 constant UINT32_MASK = type(uint32).max; diff --git a/system-contracts/contracts/libraries/TransactionHelper.sol b/system-contracts/contracts/libraries/TransactionHelper.sol index 467eb57f9..10f3a10fc 100644 --- a/system-contracts/contracts/libraries/TransactionHelper.sol +++ b/system-contracts/contracts/libraries/TransactionHelper.sol @@ -163,7 +163,9 @@ library TransactionHelper { encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. @@ -232,7 +234,9 @@ library TransactionHelper { bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // solhint-disable-next-line func-named-parameters encodedFixedLengthParams = bytes.concat( @@ -305,7 +309,9 @@ library TransactionHelper { bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); - bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); + bytes memory encodedTo = _transaction.reserved[1] == 0 + ? RLPEncoder.encodeAddress(address(uint160(_transaction.to))) + : bytes(hex"80"); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // solhint-disable-next-line func-named-parameters encodedFixedLengthParams = bytes.concat( diff --git a/system-contracts/contracts/libraries/Utils.sol b/system-contracts/contracts/libraries/Utils.sol index fc23de94b..6ea897f28 100644 --- a/system-contracts/contracts/libraries/Utils.sol +++ b/system-contracts/contracts/libraries/Utils.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {EfficientCall} from "./EfficientCall.sol"; +import {RLPEncoder} from "./RLPEncoder.sol"; import {MalformedBytecode, BytecodeError, Overflow} from "../SystemContractErrors.sol"; /** @@ -43,9 +44,23 @@ library Utils { return uint24(_x); } + function isCodeHashEVM(bytes32 _bytecodeHash) internal pure returns (bool) { + // TODO: use constants for that + return (uint8(_bytecodeHash[0]) == 2); + } + /// @return codeLength The bytecode length in bytes function bytecodeLenInBytes(bytes32 _bytecodeHash) internal pure returns (uint256 codeLength) { - codeLength = bytecodeLenInWords(_bytecodeHash) << 5; // _bytecodeHash * 32 + // TODO: use constants for that + + if (uint8(_bytecodeHash[0]) == 1) { + codeLength = bytecodeLenInWords(_bytecodeHash) << 5; // _bytecodeHash * 32 + } else if (uint8(_bytecodeHash[0]) == 2) { + // TODO: maybe rename the function + codeLength = bytecodeLenInWords(_bytecodeHash); + } else { + codeLength = 0; + } } /// @return codeLengthInWords The bytecode length in machine words @@ -110,4 +125,49 @@ library Utils { // Setting the length hashedBytecode = hashedBytecode | bytes32(lengthInWords << 224); } + + // the real max supported number is 2^16, but we'll stick to evm convention + uint256 internal constant MAX_EVM_BYTECODE_LENGTH = (2 ** 16) - 1; + + function hashEVMBytecode(bytes memory _bytecode) internal view returns (bytes32 hashedEVMBytecode) { + // solhint-disable gas-custom-errors + require(_bytecode.length <= MAX_EVM_BYTECODE_LENGTH, "po"); + + hashedEVMBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + // Setting the version of the hash + hashedEVMBytecode = (hashedEVMBytecode | bytes32(uint256(2 << 248))); + hashedEVMBytecode = hashedEVMBytecode | bytes32(_bytecode.length << 224); + } + + /// @notice Calculates the address of a deployed contract via create2 on the EVM + /// @param _sender The account that deploys the contract. + /// @param _salt The create2 salt. + /// @param _bytecodeHash The hash of the init code of the new contract. + /// @return newAddress The derived address of the account. + function getNewAddressCreate2EVM( + address _sender, + bytes32 _salt, + bytes32 _bytecodeHash + ) internal pure returns (address newAddress) { + bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), _sender, _salt, _bytecodeHash)); + + newAddress = address(uint160(uint256(hash))); + } + + /// @notice Calculates the address of a deployed contract via create + /// @param _sender The account that deploys the contract. + /// @param _senderNonce The deploy nonce of the sender's account. + function getNewAddressCreateEVM(address _sender, uint256 _senderNonce) internal pure returns (address newAddress) { + bytes memory addressEncoded = RLPEncoder.encodeAddress(_sender); + bytes memory nonceEncoded = RLPEncoder.encodeUint256(_senderNonce); + + uint256 listLength = addressEncoded.length + nonceEncoded.length; + bytes memory listLengthEncoded = RLPEncoder.encodeListLen(uint64(listLength)); + + bytes memory digest = bytes.concat(listLengthEncoded, addressEncoded, nonceEncoded); + + bytes32 hash = keccak256(digest); + newAddress = address(uint160(uint256(hash))); + } } diff --git a/system-contracts/contracts/precompiles/CodeOracle.yul b/system-contracts/contracts/precompiles/CodeOracle.yul index 63b386788..cb12af19f 100644 --- a/system-contracts/contracts/precompiles/CodeOracle.yul +++ b/system-contracts/contracts/precompiles/CodeOracle.yul @@ -29,7 +29,7 @@ object "CodeOracle" { //////////////////////////////////////////////////////////////// // HELPER FUNCTIONS //////////////////////////////////////////////////////////////// - + /// @notice The function that returns whether a certain versioned hash is marked as `known` /// @param versionedHash The versioned hash to check /// @return Whether the versioned hash is known @@ -90,7 +90,7 @@ object "CodeOracle" { // Decommitment failed revert(0,0) } - + // The "real" result of the `decommit` operation is a pointer to the memory page where the data was unpacked. // We do not know whether the data was unpacked into the memory of this contract or not. // @@ -123,15 +123,21 @@ object "CodeOracle" { } let version := shr(248, versionedCodeHash) - // Currently, only a single version of the code hash is supported: + // Currently, two versions of the code hash is supported: // 1. The standard zkEVM bytecode. It has the following format: // - hash[0] -- version (0x01) // - hash[1] -- whether the contract is being constructed // - hash[2..3] -- big endian length of the bytecode in 32-byte words. This number must be odd. // - hash[4..31] -- the last 28 bytes of the sha256 hash. + // 2. EVM bytecode. It has the following format: + // - hash[0] -- version (0x02) + // - hash[1] -- whether the contract is being constructed + // - hash[2..3] -- big endian length of the bytecode in bytes. This number can be arbitrary. + // - hash[4..31] -- the last 28 bytes of the sha256 hash. // - // Note, that in theory it can represent just some random blob of bytes, while - // in practice it only represents only the corresponding bytecodes. + // Note, that in theory both values can represent just some random blob of bytes, while + // in practice they only represent only the corresponding bytecodes. + switch version case 1 { @@ -140,6 +146,14 @@ object "CodeOracle" { let lengthInWords := and(shr(224, versionedCodeHash), 0xffff) decommit(versionedCodeHash, lengthInWords) } + case 2 { + // We do not double check whether the length is 32 mod 64, since it assumed that only valid bytecodes + // can pass the `isCodeHashKnown` check. + let lengthInBytes := and(shr(224, versionedCodeHash), 0xffff) + + // It is assumed that the `lengthInBytes` is divisible by 32. + decommit(versionedCodeHash, div(lengthInBytes, 32)) + } default { // Unsupported revert(0,0) diff --git a/system-contracts/evm-interpreter/EvmInterpreter.template.yul b/system-contracts/evm-interpreter/EvmInterpreter.template.yul new file mode 100644 index 000000000..0aed3e039 --- /dev/null +++ b/system-contracts/evm-interpreter/EvmInterpreter.template.yul @@ -0,0 +1,169 @@ +object "EVMInterpreter" { + code { + /// @dev This function is used to get the initCode. + /// @dev It assumes that the initCode has been passed via the calldata and so we use the pointer + /// to obtain the bytecode. + function getConstructorBytecode() { + let bytecodeLengthOffset := BYTECODE_OFFSET() + let bytecodeOffset := add(BYTECODE_OFFSET(), 32) + + loadCalldataIntoActivePtr() + + let size := getActivePtrDataSize() + mstore(bytecodeLengthOffset, size) + + copyActivePtrData(bytecodeOffset, 0, size) + } + + // Note that this function modifies EVM memory and does not restore it. It is expected that + // it is the last called function during execution. + function setDeployedCode(gasLeft, offset, len) { + // This error should never be triggered + // require(offset > 100, "Offset too small"); + + mstore(sub(offset, 100), 0xD9EB76B200000000000000000000000000000000000000000000000000000000) + mstore(sub(offset, 96), gasLeft) + mstore(sub(offset, 64), 0x40) + mstore(sub(offset, 32), len) + + let farCallAbi := getFarCallABI( + 0, + 0, + sub(offset, 100), + add(len, 100), + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := DEPLOYER_SYSTEM_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function padBytecode(offset, len) -> blobOffset, blobLen { + blobOffset := sub(offset, 32) + let trueLastByte := add(offset, len) + + mstore(blobOffset, len) + // clearing out additional bytes + mstore(trueLastByte, 0) + mstore(add(trueLastByte, 32), 0) + + blobLen := add(len, 32) + + if iszero(eq(mod(blobLen, 32), 0)) { + blobLen := add(blobLen, sub(32, mod(blobLen, 32))) + } + + // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd + if iszero(eq(mod(blobLen, 64), 32)) { + blobLen := add(blobLen, 32) + } + } + + function validateCorrectBytecode(offset, len, gasToReturn) -> returnGas { + if len { + let firstByte := shr(248, mload(offset)) + if eq(firstByte, 0xEF) { + revert(0, 0) + } + } + + let gasForCode := mul(len, 200) + returnGas := chargeGas(gasToReturn, gasForCode) + } + + + + function simulate( + isCallerEVM, + evmGasLeft, + isStatic, + ) -> returnOffset, returnLen, retGasLeft { + + returnOffset := MEM_OFFSET_INNER() + returnLen := 0 + + + + retGasLeft := evmGasLeft + } + + //////////////////////////////////////////////////////////////// + // FALLBACK + //////////////////////////////////////////////////////////////// + + let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() + + if isStatic { + revert(0, 0) + } + + getConstructorBytecode() + + if iszero(isCallerEVM) { + evmGasLeft := getEVMGas() + } + + let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) + + gasToReturn := validateCorrectBytecode(offset, len, gasToReturn) + + offset, len := padBytecode(offset, len) + + setDeployedCode(gasToReturn, offset, len) + } + object "EVMInterpreter_deployed" { + code { + + + function $llvm_NoInline_llvm$_simulate( + isCallerEVM, + evmGasLeft, + isStatic, + ) -> returnOffset, returnLen { + + returnOffset := MEM_OFFSET_INNER() + returnLen := 0 + + + + if eq(isCallerEVM, 1) { + // Includes gas + returnOffset := sub(returnOffset, 32) + checkOverflow(returnLen, 32, evmGasLeft) + returnLen := add(returnLen, 32) + + mstore(returnOffset, evmGasLeft) + } + } + + //////////////////////////////////////////////////////////////// + // FALLBACK + //////////////////////////////////////////////////////////////// + + let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() + + if iszero(isCallerEVM) { + evmGasLeft := getEVMGas() + isStatic := getIsStaticFromCallFlags() + } + + // First, copy the contract's bytecode to be executed into tEdhe `BYTECODE_OFFSET` + // segment of memory. + getDeployedBytecode() + + pop($llvm_AlwaysInline_llvm$_warmAddress(address())) + + let returnOffset, returnLen := $llvm_NoInline_llvm$_simulate(isCallerEVM, evmGasLeft, isStatic) + return(returnOffset, returnLen) + } + } +} diff --git a/system-contracts/evm-interpreter/EvmInterpreterFunctions.template.yul b/system-contracts/evm-interpreter/EvmInterpreterFunctions.template.yul new file mode 100644 index 000000000..4d2f6f3aa --- /dev/null +++ b/system-contracts/evm-interpreter/EvmInterpreterFunctions.template.yul @@ -0,0 +1,1357 @@ +function ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008002 +} + +function NONCE_HOLDER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008003 +} + +function DEPLOYER_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008006 +} + +function CODE_ORACLE_SYSTEM_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008012 +} + +function EVM_GAS_MANAGER_CONTRACT() -> addr { + addr := 0x0000000000000000000000000000000000008013 +} + +function DEBUG_SLOT_OFFSET() -> offset { + offset := mul(32, 32) // TODO cleanup +} + +function LAST_RETURNDATA_SIZE_OFFSET() -> offset { + offset := add(DEBUG_SLOT_OFFSET(), mul(5, 32)) +} + +function STACK_OFFSET() -> offset { + offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 32) +} + +function BYTECODE_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1024, 32)) +} + +function INF_PASS_GAS() -> inf { + inf := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +} + +function MAX_POSSIBLE_BYTECODE() -> max { + max := 32000 +} + +function MEM_OFFSET() -> offset { + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) +} + +function MEM_OFFSET_INNER() -> offset { + offset := add(MEM_OFFSET(), 32) +} + +function MAX_POSSIBLE_MEM() -> max { + max := 0x100000 // 1MB +} + +function MAX_MEMORY_FRAME() -> max { + max := add(MEM_OFFSET_INNER(), MAX_POSSIBLE_MEM()) +} + +function MAX_UINT() -> max_uint { + max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +} + +// Essentially a NOP that will not get optimized away by the compiler +function $llvm_NoInline_llvm$_unoptimized() { + pop(1) +} + +function printHex(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebde) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() +} + +function printString(value) { + mstore(add(DEBUG_SLOT_OFFSET(), 0x20), 0x00debdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdebdf) + mstore(add(DEBUG_SLOT_OFFSET(), 0x40), value) + mstore(DEBUG_SLOT_OFFSET(), 0x4A15830341869CAA1E99840C97043A1EA15D2444DA366EFFF5C43B4BEF299681) + $llvm_NoInline_llvm$_unoptimized() +} + +// It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 +function readIP(ip,maxAcceptablePos) -> opcode { + if gt(ip, maxAcceptablePos) { + revert(0, 0) + } + + opcode := and(mload(sub(ip, 31)), 0xff) +} + +function readBytes(start, maxAcceptablePos,length) -> value { + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) + } + value := shr(mul(8,sub(32,length)),mload(start)) +} + +function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, sub(position, 1))) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + let dup := mload(tempSp) + + newSp := add(sp, 0x20) + mstore(newSp, dup) +} + +function swapStackItem(sp, evmGas, position) -> evmGasLeft { + evmGasLeft := chargeGas(evmGas, 3) + let tempSp := sub(sp, mul(0x20, position)) + + if or(gt(tempSp, BYTECODE_OFFSET()), eq(tempSp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + if lt(tempSp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + + let s2 := mload(sp) + let s1 := mload(tempSp) + + mstore(sp, s1) + mstore(tempSp, s2) +} + +function popStackItem(sp, evmGasLeft) -> a, newSp { + // We can not return any error here, because it would break compatibility + if lt(sp, STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } + + a := mload(sp) + newSp := sub(sp, 0x20) +} + +function pushStackItem(sp, item, evmGasLeft) -> newSp { + if or(gt(sp, BYTECODE_OFFSET()), eq(sp, BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + newSp := add(sp, 0x20) + mstore(newSp, item) +} + +function popStackItemWithoutCheck(sp) -> a, newSp { + a := mload(sp) + newSp := sub(sp, 0x20) +} + +function pushStackItemWithoutCheck(sp, item) -> newSp { + newSp := add(sp, 0x20) + mstore(newSp, item) +} + +function popStackCheck(sp, evmGasLeft, numInputs) { + if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { + revertWithGas(evmGasLeft) + } +} + +function pushStackCheck(sp, evmGasLeft, numInputs) { + if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_OFFSET())) { + revertWithGas(evmGasLeft) + } +} + +function getCodeAddress() -> addr { + addr := verbatim_0i_1o("code_source") +} + +function loadReturndataIntoActivePtr() { + verbatim_0i_0o("return_data_ptr_to_active") +} + +function loadCalldataIntoActivePtr() { + verbatim_0i_0o("calldata_ptr_to_active") +} + +function getActivePtrDataSize() -> size { + size := verbatim_0i_1o("active_ptr_data_size") +} + +function copyActivePtrData(_dest, _source, _size) { + verbatim_3i_0o("active_ptr_data_copy", _dest, _source, _size) +} + +function ptrAddIntoActive(_dest) { + verbatim_1i_0o("active_ptr_add_assign", _dest) +} + +function ptrShrinkIntoActive(_dest) { + verbatim_1i_0o("active_ptr_shrink_assign", _dest) +} + +function _getRawCodeHash(account) -> hash { + mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) + mstore(4, account) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + hash := mload(0) +} + +function getIsStaticFromCallFlags() -> isStatic { + isStatic := verbatim_0i_1o("get_global::call_flags") + isStatic := iszero(iszero(and(isStatic, 0x04))) +} + +// Basically performs an extcodecopy, while returning the length of the bytecode. +function _fetchDeployedCode(addr, _offset, _len) -> codeLen { + codeLen := _fetchDeployedCodeWithDest(addr, 0, _len, _offset) +} + +// Basically performs an extcodecopy, while returning the length of the bytecode. +function _fetchDeployedCodeWithDest(addr, _offset, _len, dest) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + + if gt(_len, codeLen) { + _len := codeLen + } + + returndatacopy(dest, add(32,_offset), _len) +} + +// Returns the length of the bytecode. +function _fetchDeployedCodeLen(addr) -> codeLen { + let codeHash := _getRawCodeHash(addr) + + mstore(0, codeHash) + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + + switch iszero(success) + case 1 { + // The code oracle call can only fail in the case where the contract + // we are querying is the current one executing and it has not yet been + // deployed, i.e., if someone calls codesize (or extcodesize(address())) + // inside the constructor. In that case, code length is zero. + codeLen := 0 + } + default { + // The first word is the true length of the bytecode + returndatacopy(0, 0, 32) + codeLen := mload(0) + } +} + +function getDeployedBytecode() { + let codeLen := _fetchDeployedCode( + getCodeAddress(), + add(BYTECODE_OFFSET(), 32), + MAX_POSSIBLE_BYTECODE() + ) + + mstore(BYTECODE_OFFSET(), codeLen) +} + +function consumeEvmFrame() -> passGas, isStatic, callerEVM { + // function consumeEvmFrame() external returns (uint256 passGas, bool isStatic) + mstore(0, 0x04C14E9E00000000000000000000000000000000000000000000000000000000) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // Should never happen + revert(0, 0) + } + + returndatacopy(0,0,64) + + passGas := mload(0) + isStatic := mload(32) + + if iszero(eq(passGas, INF_PASS_GAS())) { + callerEVM := true + } +} + +function chargeGas(prevGas, toCharge) -> gasRemaining { + if lt(prevGas, toCharge) { + revertWithGas(0) + } + + gasRemaining := sub(prevGas, toCharge) +} + +function getMax(a, b) -> max { + max := b + if gt(a, b) { + max := a + } +} + +function bitLength(n) -> bitLen { + for { } gt(n, 0) { } { // while(n > 0) + if iszero(n) { + bitLen := 1 + break + } + n := shr(1, n) + bitLen := add(bitLen, 1) + } +} + +function bitMaskFromBytes(nBytes) -> bitMask { + bitMask := sub(exp(2, mul(nBytes, 8)), 1) // 2**(nBytes*8) - 1 +} +// The gas cost mentioned here is purely the cost of the contract, +// and does not consider the cost of the call itself nor the instructions +// to put the parameters in memory. +// Take into account MEM_OFFSET_INNER() when passing the argsOffset +function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { + switch addr + case 0x01 { // ecRecover + gasToCharge := 3000 + } + case 0x02 { // SHA2-256 + gasToCharge := 60 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(12, dataWordSize)) + } + case 0x03 { // RIPEMD-160 + gasToCharge := 600 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(120, dataWordSize)) + } + case 0x04 { // identity + gasToCharge := 15 + let dataWordSize := shr(5, add(argsSize, 31)) // (argsSize+31)/32 + gasToCharge := add(gasToCharge, mul(3, dataWordSize)) + } + // [0; 31] (32 bytes) Bsize Byte size of B + // [32; 63] (32 bytes) Esize Byte size of E + // [64; 95] (32 bytes) Msize Byte size of M + /* + def calculate_iteration_count(exponent_length, exponent): + iteration_count = 0 + if exponent_length <= 32 and exponent == 0: iteration_count = 0 + elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + return max(iteration_count, 1) + def calculate_gas_cost(base_length, modulus_length, exponent_length, exponent): + multiplication_complexity = calculate_multiplication_complexity(base_length, modulus_length) + iteration_count = calculate_iteration_count(exponent_length, exponent) + return max(200, math.floor(multiplication_complexity * iteration_count / 3)) + */ + // modexp gas cost EIP below + // https://eips.ethereum.org/EIPS/eip-2565 + case 0x05 { // modexp + let mulComplex + let Bsize := mload(argsOffset) + let Esize := mload(add(argsOffset, 0x20)) + + { + let words := getMax(Bsize, mload(add(argsOffset, 0x40))) // shr(3, x) == x/8 + if and(lt(words, 64), eq(words, 64)){ + // if x <= 64: return x ** 2 + mulComplex := mul(words, words) + } + if and(and(lt(words, 1024), eq(words, 1024)), gt(words, 64)){ + // elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 + mulComplex := sub(add(shr(2, mul(words, words)), mul(96, words)), 3072) + } + if gt(words, 64) { + // else: return x ** 2 // 16 + 480 * x - 199680 + mulComplex := sub(add(shr(4, mul(words, words)), mul(480, words)), 199680) + } + } + + // [96 + Bsize; 96 + Bsize + Esize] E + let exponentFirst256, exponentIsZero, exponentBitLen + if or(lt(Esize, 32), eq(Esize, 32)) { + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentBitLen := bitLength(exponentFirst256) + exponentIsZero := iszero(and(exponentFirst256, bitMaskFromBytes(Esize))) + } + if gt(Esize, 32) { + exponentFirst256 := mload(add(add(argsOffset, 0x60), Bsize)) + exponentIsZero := iszero(exponentFirst256) + let exponentNext + // This is done because the first 32bytes of the exponent were loaded + for { let i := 0 } lt(i, div(Esize, 32)) { i := add(i, 1) Esize := sub(Esize, 32) } { // check every 32bytes + // Maybe there isn't exactly 32 bytes, so a mask should be applied + exponentNext := mload(add(add(add(argsOffset, 0x60), Bsize), add(mul(i, 32), 32))) + exponentBitLen := add(bitLength(exponentNext), mul(mul(32, 8), add(i, 1))) + if iszero(iszero(and(exponentNext, bitMaskFromBytes(Esize)))) { + exponentIsZero := false + } + } + } + + // if exponent_length <= 32 and exponent == 0: iteration_count = 0 + // return max(iteration_count, 1) + let iterationCount := 1 + // elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + if and(lt(Esize, 32), iszero(exponentIsZero)) { + iterationCount := sub(exponentBitLen, 1) + } + // elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + if gt(Esize, 32) { + iterationCount := add(mul(8, sub(Esize, 32)), sub(bitLength(and(exponentFirst256, MAX_UINT())), 1)) + } + + gasToCharge := getMax(200, div(mul(mulComplex, iterationCount), 3)) + } + // ecAdd ecMul ecPairing EIP below + // https://eips.ethereum.org/EIPS/eip-1108 + case 0x06 { // ecAdd + // The gas cost is fixed at 150. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 150 + } + case 0x07 { // ecMul + // The gas cost is fixed at 6000. However, if the input + // does not allow to compute a valid result, all the gas sent is consumed. + gasToCharge := 6000 + } + // 35,000 * k + 45,000 gas, where k is the number of pairings being computed. + // The input must always be a multiple of 6 32-byte values. + case 0x08 { // ecPairing + gasToCharge := 45000 + let k := div(argsSize, 0xC0) // 0xC0 == 6*32 + gasToCharge := add(gasToCharge, mul(k, 35000)) + } + case 0x09 { // blake2f + // argsOffset[0; 3] (4 bytes) Number of rounds (big-endian uint) + gasToCharge := and(mload(argsOffset), 0xFFFFFFFF) // last 4bytes + } + default { + gasToCharge := 0 + } +} + +function checkMemOverflowByOffset(offset, evmGasLeft) { + if gt(offset, MAX_POSSIBLE_MEM()) { + mstore(0, evmGasLeft) + revert(0, 32) + } +} + +function checkMemOverflow(location, evmGasLeft) { + if gt(location, MAX_MEMORY_FRAME()) { + mstore(0, evmGasLeft) + revert(0, 32) + } +} + +function checkOverflow(data1, data2, evmGasLeft) { + if lt(add(data1, data2), data2) { + revertWithGas(evmGasLeft) + } +} + +function revertWithGas(evmGasLeft) { + mstore(0, evmGasLeft) + revert(0, 32) +} + +// This function can overflow, it is the job of the caller to ensure that it does not. +// The argument to this function is the offset into the memory region IN BYTES. +function expandMemory(newSize) -> gasCost { + let oldSizeInWords := mload(MEM_OFFSET()) + + // The add 31 here before dividing is there to account for misaligned + // memory expansions, where someone calls this with a newSize that is not + // a multiple of 32. For instance, if someone calls it with an offset of 33, + // the new size in words should be 2, not 1, but dividing by 32 will give 1. + // Adding 31 solves it. + let newSizeInWords := div(add(newSize, 31), 32) + + if gt(newSizeInWords, oldSizeInWords) { + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) + + mstore(MEM_OFFSET(), newSizeInWords) + } +} + +function isSlotWarm(key) -> isWarm { + mstore(0, 0x482D2E7400000000000000000000000000000000000000000000000000000000) + mstore(4, key) + + let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isWarm := mload(0) +} + +function warmSlot(key,currentValue) -> isWarm, originalValue { + mstore(0, 0xBDF7816000000000000000000000000000000000000000000000000000000000) + mstore(4, key) + mstore(36,currentValue) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 64) + + isWarm := mload(0) + originalValue := mload(32) +} + +function getFarCallABI( + dataOffset, + memoryPage, + dataStart, + dataLength, + gasPassed, + shardId, + forwardingMode, + isConstructorCall, + isSystemCall +) -> ret { + let farCallAbi := 0 + farCallAbi := or(farCallAbi, dataOffset) + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gasPassed)) + farCallAbi := or(farCallAbi, shl(224, shardId)) + farCallAbi := or(farCallAbi, shl(232, forwardingMode)) + farCallAbi := or(farCallAbi, shl(248, 1)) + ret := farCallAbi +} + +function addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) -> newOffset,newSize { + newOffset := offset + newSize := size + if eq(isCallerEVM,1) { + // include gas + let previousValue := mload(sub(offset,32)) + mstore(sub(offset,32),evmGasLeft) + //mstore(sub(offset,32),previousValue) // Im not sure why this is needed, it was like this in the solidity code, + // but it appears to rewrite were we want to store the gas + + newOffset := sub(offset, 32) + newSize := add(size, 32) + } +} + +function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { + mstore(0, 0x8DB2BA7800000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 36, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + returndatacopy(0, 0, 32) + isWarm := mload(0) +} + +function getRawNonce(addr) -> nonce { + mstore(0, 0x5AA9B6B500000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) +} + +function _isEVM(_addr) -> isEVM { + // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) + // function isAccountEVM(address _addr) external view returns (bool); + // IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( + // address(SYSTEM_CONTRACTS_OFFSET + 0x02) + // ); + + mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) + mstore(4, _addr) + + let success := staticcall(gas(), ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + isEVM := mload(0) +} + +function _pushEVMFrame(_passGas, _isStatic) { + // function pushEVMFrame(uint256 _passGas, bool _isStatic) external + + mstore(0, 0xEAD7715600000000000000000000000000000000000000000000000000000000) + mstore(4, _passGas) + mstore(36, _isStatic) + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 68, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } +} + +function _popEVMFrame() { + // function popEVMFrame() external + + let farCallAbi := getFarCallABI( + 0, + 0, + 0, + 4, + gas(), + // Only rollup is supported for now + 0, + 0, + 0, + 1 + ) + + let to := EVM_GAS_MANAGER_CONTRACT() + + mstore(0, 0xE467D2F000000000000000000000000000000000000000000000000000000000) + + let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) + if iszero(success) { + // This error should never happen + revert(0, 0) + } +} + +// Each evm gas is 5 zkEVM one +function GAS_DIVISOR() -> gas_div { gas_div := 5 } +function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 +function OVERHEAD() -> overhead { overhead := 2000 } + +// From precompiles/CodeOracle +function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } +function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + +function _calcEVMGas(_zkevmGas) -> calczkevmGas { + calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) +} + +function getEVMGas() -> evmGas { + let _gas := gas() + let requiredGas := add(EVM_GAS_STIPEND(), OVERHEAD()) + + switch lt(_gas, requiredGas) + case 1 { + evmGas := 0 + } + default { + evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) + } +} + +function _getZkEVMGas(_evmGas, addr) -> zkevmGas { + zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } +} + +function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + let rtsz := returndatasize() + + loadReturndataIntoActivePtr() + + // if (rtsz > 31) + switch gt(rtsz, 31) + case 0 { + // Unexpected return data. + _gasLeft := 0 + _eraseReturndataPointer() + } + default { + returndatacopy(0, 0, 32) + _gasLeft := mload(0) + + // We copy as much returndata as possible without going over the + // returndata size. + switch lt(sub(rtsz, 32), _outputLen) + case 0 { returndatacopy(_outputOffset, 32, _outputLen) } + default { returndatacopy(_outputOffset, 32, sub(rtsz, 32)) } + + mstore(lastRtSzOffset, sub(rtsz, 32)) + + // Skip the returnData + ptrAddIntoActive(32) + } +} + +function _eraseReturndataPointer() { + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + let activePtrSize := getActivePtrDataSize() + ptrShrinkIntoActive(and(activePtrSize, 0xFFFFFFFF))// uint32(activePtrSize) + mstore(lastRtSzOffset, 0) +} + +function _saveReturndataAfterZkEVMCall() { + loadReturndataIntoActivePtr() + let lastRtSzOffset := LAST_RETURNDATA_SIZE_OFFSET() + + mstore(lastRtSzOffset, returndatasize()) +} + +function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp { + let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + + popStackCheck(oldSp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + if gt(gasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } + + let frameGasLeft + let success + switch _isEVM(addr) + case 0 { + // zkEVM native + gasToPass := _getZkEVMGas(gasToPass, addr) + let zkevmGasBefore := gas() + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPass, gasUsed) { + frameGasLeft := sub(gasToPass, gasUsed) + } + } + default { + _pushEVMFrame(gasToPass, true) + success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, 0, 0) + + frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + _popEVMFrame() + } + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + + sp := pushStackItem(sp, success, evmGasLeft) +} +function capGas(evmGasLeft,oldGasToPass) -> gasToPass { + let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 + gasToPass := oldGasToPass + if gt(oldGasToPass, maxGasToPass) { + gasToPass := maxGasToPass + } +} + +function getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) -> maxExpand{ + maxExpand := add(retOffset, retSize) + switch lt(maxExpand,add(argsOffset, argsSize)) + case 0 { + maxExpand := expandMemory(maxExpand) + } + default { + maxExpand := expandMemory(add(argsOffset, argsSize)) + } +} + +function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + + switch isStatic + case 0 { + switch is_evm + case 0 { + // zkEVM native + gasToPassNew := _getZkEVMGas(gasToPassNew, addr) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } + default { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(EVM_GAS_STIPEND(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + } + default { + if value { + revertWithGas(gasToPassNew) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } +} + +function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { + let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize + + popStackCheck(oldSp, evmGasLeft, 7) + gasToPass, sp := popStackItemWithoutCheck(oldSp) + addr, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + // static_gas = 0 + // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost + // code_execution_cost is the cost of the called code execution (limited by the gas parameter). + // If address is warm, then address_access_cost is 100, otherwise it is 2600. See section access sets. + // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. 2300 is thus removed from the cost, and also added to the gas input. + // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + if gt(value, 0) { + extraCost := add(extraCost,6700) + gasToPass := add(gasToPass,2300) + } + + if and(isAddrEmpty(addr), gt(value, 0)) { + extraCost := add(extraCost,25000) + } + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + argsOffset := add(argsOffset,MEM_OFFSET_INNER()) + retOffset := add(retOffset,MEM_OFFSET_INNER()) + + checkOverflow(argsOffset,argsSize, evmGasLeft) + checkOverflow(retOffset,retSize, evmGasLeft) + + checkMemOverflow(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflow(add(retOffset, retSize), evmGasLeft) + + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp,success, evmGasLeft) +} + +function delegateCall(oldSp, oldIsStatic, evmGasLeft) -> sp, isStatic, extraCost { + let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + + sp := oldSp + isStatic := oldIsStatic + + popStackCheck(sp, evmGasLeft, 6) + gasToPass, sp := popStackItemWithoutCheck(sp) + addr, sp := popStackItemWithoutCheck(sp) + argsOffset, sp := popStackItemWithoutCheck(sp) + argsSize, sp := popStackItemWithoutCheck(sp) + retOffset, sp := popStackItemWithoutCheck(sp) + retSize, sp := popStackItemWithoutCheck(sp) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkOverflow(argsOffset, argsSize, evmGasLeft) + checkOverflow(retOffset, retSize, evmGasLeft) + + checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft) + checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft) + + if iszero(_isEVM(addr)) { + revertWithGas(evmGasLeft) + } + + extraCost := 0 + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + extraCost := 2500 + } + + { + let maxExpand := getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) + extraCost := add(extraCost,maxExpand) + } + gasToPass := capGas(evmGasLeft,gasToPass) + + _pushEVMFrame(gasToPass, isStatic) + let success := delegatecall( + // We can not just pass all gas here to prevent overflow of zkEVM gas counter + EVM_GAS_STIPEND(), + addr, + add(MEM_OFFSET_INNER(), argsOffset), + argsSize, + 0, + 0 + ) + + let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize) + + _popEVMFrame() + + let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + switch iszero(precompileCost) + case 1 { + extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) + } + default { + extraCost := add(extraCost, precompileCost) + } + sp := pushStackItem(sp, success, evmGasLeft) +} + +function _performStaticCall( + _calleeIsEVM, + _calleeGas, + _callee, + _inputOffset, + _inputLen, + _outputOffset, + _outputLen +) -> success, _gasLeft { + switch _calleeIsEVM + case 0 { + // zkEVM native + _calleeGas := _getZkEVMGas(_calleeGas, _callee) + let zkevmGasBefore := gas() + success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) + + _saveReturndataAfterZkEVMCall() + + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + _gasLeft := 0 + if gt(_calleeGas, gasUsed) { + _gasLeft := sub(_calleeGas, gasUsed) + } + } + default { + _pushEVMFrame(_calleeGas, true) + success := staticcall(EVM_GAS_STIPEND(), _callee, _inputOffset, _inputLen, 0, 0) + + _gasLeft := _saveReturndataAfterEVMCall(_outputOffset, _outputLen) + _popEVMFrame() + } +} + +function isAddrEmpty(addr) -> isEmpty { + isEmpty := 0 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } + } +} + +function _fetchConstructorReturnGas() -> gasLeft { + mstore(0, 0x24E5AB4A00000000000000000000000000000000000000000000000000000000) + + let success := staticcall(gas(), DEPLOYER_SYSTEM_CONTRACT(), 0, 4, 0, 32) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + + gasLeft := mload(0) +} + +function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt) -> result, evmGasLeft, addr { + pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) + + _eraseReturndataPointer() + + let gasForTheCall := capGas(evmGasLeftOld,INF_PASS_GAS()) + + if lt(selfbalance(),value) { + revertWithGas(evmGasLeftOld) + } + + offset := add(MEM_OFFSET_INNER(), offset) + + pushStackCheck(sp, evmGasLeftOld, 4) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40))) + sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20))) + + _pushEVMFrame(gasForTheCall, false) + + if isCreate2 { + // Create2EVM selector + mstore(sub(offset, 0x80), 0x4e96f4c0) + // salt + mstore(sub(offset, 0x60), salt) + // Where the arg starts (third word) + mstore(sub(offset, 0x40), 0x40) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x64), add(size, 0x64), 0, 32) + } + + + if iszero(isCreate2) { + // CreateEVM selector + mstore(sub(offset, 0x60), 0xff311601) + // Where the arg starts (second word) + mstore(sub(offset, 0x40), 0x20) + // Length of the init code + mstore(sub(offset, 0x20), size) + + + result := call(gas(), DEPLOYER_SYSTEM_CONTRACT(), value, sub(offset, 0x44), add(size, 0x44), 0, 32) + } + + addr := mload(0) + + let gasLeft + switch result + case 0 { + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) + + _popEVMFrame() + + let back + + // skipping check since we pushed exactly 4 items earlier + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x20), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x40), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x60), back) + back, sp := popStackItemWithoutCheck(sp) + mstore(sub(offset, 0x80), back) +} + +function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { + let rest_bits := shl(3, len) + let upper_bits := sub(256, rest_bits) + let val_mask := shl(upper_bits, MAX_UINT()) + let val_masked := and(val, val_mask) + let dst_val := mload(dest) + let dst_mask := shr(rest_bits, MAX_UINT()) + let dst_masked := and(dst_val, dst_mask) + mstore(dest, or(val_masked, dst_masked)) +} + +function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { + let dest_addr := dest + let src_addr := src + let dest_end := add(dest, and(len, sub(0, 32))) + for { } lt(dest_addr, dest_end) {} { + mstore(dest_addr, mload(src_addr)) + dest_addr := add(dest_addr, 32) + src_addr := add(src_addr, 32) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + } +} + +function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { + let dest_end := add(dest, and(len, sub(0, 32))) + for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { + mstore(i, 0) + } + + let rest_len := and(len, 31) + if rest_len { + $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + } +} + +function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + popStackCheck(oldSp, evmGasLeft, 4) + addr, sp := popStackItemWithoutCheck(oldSp) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) + + // Gets the code from the addr + if and(iszero(iszero(_getRawCodeHash(addr))),gt(len,0)) { + pop(_fetchDeployedCodeWithDest(addr, offset, len,add(dest,MEM_OFFSET_INNER()))) + } +} + +function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size + + popStackCheck(oldSp, evmGasLeft, 3) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + let dynamicGas := add( + shr(4, add(size, 31)), + expandMemory(add(offset, size)) + ) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let result, addr + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0) + + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } +} + +function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let value, offset, size, salt + + popStackCheck(oldSp, evmGasLeft, 4) + value, sp := popStackItemWithoutCheck(oldSp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + salt, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revertWithGas(evmGasLeft) + } + + if gt(value, balance(address())) { + revertWithGas(evmGasLeft) + } + + // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // minimum_word_size = (size + 31) / 32 + // init_code_cost = 2 * minimum_word_size + // hash_cost = 6 * minimum_word_size + // code_deposit_cost = 200 * deployed_code_size + evmGasLeft := chargeGas(evmGasLeft, add( + expandMemory(add(offset, size)), + shr(2, add(size, 31)) + )) + + result, evmGasLeft, addr := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft,true,salt) +} diff --git a/system-contracts/evm-interpreter/EvmInterpreterLoop.template.yul b/system-contracts/evm-interpreter/EvmInterpreterLoop.template.yul new file mode 100644 index 000000000..ae64735c0 --- /dev/null +++ b/system-contracts/evm-interpreter/EvmInterpreterLoop.template.yul @@ -0,0 +1,1467 @@ +// stack pointer - index to first stack element; empty stack = -1 +let sp := sub(STACK_OFFSET(), 32) +// instruction pointer - index to next instruction. Not called pc because it's an +// actual yul/evm instruction. +let ip := add(BYTECODE_OFFSET(), 32) +let opcode + +let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + +for { } true { } { + opcode := readIP(ip,maxAcceptablePos) + + switch opcode + case 0x00 { // OP_STOP + break + } + case 0x01 { // OP_ADD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, add(a, b)) + ip := add(ip, 1) + } + case 0x02 { // OP_MUL + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mul(a, b)) + ip := add(ip, 1) + } + case 0x03 { // OP_SUB + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sub(a, b)) + ip := add(ip, 1) + } + case 0x04 { // OP_DIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, div(a, b)) + ip := add(ip, 1) + } + case 0x05 { // OP_SDIV + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sdiv(a, b)) + ip := add(ip, 1) + } + case 0x06 { // OP_MOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, mod(a, b)) + ip := add(ip, 1) + } + case 0x07 { // OP_SMOD + evmGasLeft := chargeGas(evmGasLeft, 5) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, smod(a, b)) + ip := add(ip, 1) + } + case 0x08 { // OP_ADDMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, addmod(a, b, N)) + ip := add(ip, 1) + } + case 0x09 { // OP_MULMOD + evmGasLeft := chargeGas(evmGasLeft, 8) + + let a, b, N + + popStackCheck(sp, evmGasLeft, 3) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + N, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItem(sp, mulmod(a, b, N), evmGasLeft) + ip := add(ip, 1) + } + case 0x0A { // OP_EXP + evmGasLeft := chargeGas(evmGasLeft, 10) + + let a, exponent + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + exponent, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, exp(a, exponent)) + + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) + ip := add(ip, 1) + } + case 0x0B { // OP_SIGNEXTEND + evmGasLeft := chargeGas(evmGasLeft, 5) + + let b, x + + popStackCheck(sp, evmGasLeft, 2) + b, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, signextend(b, x)) + ip := add(ip, 1) + } + case 0x10 { // OP_LT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, lt(a, b)) + ip := add(ip, 1) + } + case 0x11 { // OP_GT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, gt(a, b)) + ip := add(ip, 1) + } + case 0x12 { // OP_SLT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, slt(a, b)) + ip := add(ip, 1) + } + case 0x13 { // OP_SGT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sgt(a, b)) + ip := add(ip, 1) + } + case 0x14 { // OP_EQ + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, eq(a, b)) + ip := add(ip, 1) + } + case 0x15 { // OP_ISZERO + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, iszero(a)) + ip := add(ip, 1) + } + case 0x16 { // OP_AND + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, and(a,b)) + ip := add(ip, 1) + } + case 0x17 { // OP_OR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, or(a,b)) + ip := add(ip, 1) + } + case 0x18 { // OP_XOR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a, b + + popStackCheck(sp, evmGasLeft, 2) + a, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, xor(a, b)) + ip := add(ip, 1) + } + case 0x19 { // OP_NOT + evmGasLeft := chargeGas(evmGasLeft, 3) + + let a + + popStackCheck(sp, evmGasLeft, 1) + a, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, not(a)) + ip := add(ip, 1) + } + case 0x1A { // OP_BYTE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i, x + + popStackCheck(sp, evmGasLeft, 2) + i, sp := popStackItemWithoutCheck(sp) + x, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, byte(i, x)) + ip := add(ip, 1) + } + case 0x1B { // OP_SHL + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shl(shift, value)) + ip := add(ip, 1) + } + case 0x1C { // OP_SHR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, shr(shift, value)) + ip := add(ip, 1) + } + case 0x1D { // OP_SAR + evmGasLeft := chargeGas(evmGasLeft, 3) + + let shift, value + + popStackCheck(sp, evmGasLeft, 2) + shift, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, sar(shift, value)) + ip := add(ip, 1) + } + case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) + + let offset, size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + let keccak := keccak256(add(MEM_OFFSET_INNER(), offset), size) + + // When an offset is first accessed (either read or write), memory may trigger + // an expansion, which costs gas. + // dynamicGas = 6 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + sp := pushStackItem(sp, keccak, evmGasLeft) + ip := add(ip, 1) + } + case 0x30 { // OP_ADDRESS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, address(), evmGasLeft) + ip := add(ip, 1) + } + case 0x31 { // OP_BALANCE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + sp := pushStackItemWithoutCheck(sp, balance(addr)) + ip := add(ip, 1) + } + case 0x32 { // OP_ORIGIN + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, origin(), evmGasLeft) + ip := add(ip, 1) + } + case 0x33 { // OP_CALLER + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, caller(), evmGasLeft) + ip := add(ip, 1) + } + case 0x34 { // OP_CALLVALUE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, callvalue(), evmGasLeft) + ip := add(ip, 1) + } + case 0x35 { // OP_CALLDATALOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let i + + popStackCheck(sp, evmGasLeft, 1) + i, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, calldataload(i)) + ip := add(ip, 1) + } + case 0x36 { // OP_CALLDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, calldatasize(), evmGasLeft) + ip := add(ip, 1) + } + case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let destOffset, offset, size + + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(destOffset,size), evmGasLeft) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + ip := add(ip, 1) + + } + case 0x38 { // OP_CODESIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let bytecodeLen := mload(BYTECODE_OFFSET()) + sp := pushStackItem(sp, bytecodeLen, evmGasLeft) + ip := add(ip, 1) + } + case 0x39 { // OP_CODECOPY + + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dst, offset, len + + popStackCheck(sp, evmGasLeft, 3) + dst, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + // minimum_word_size = (size + 31) / 32 + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + dst := add(dst, MEM_OFFSET_INNER()) + offset := add(add(offset, BYTECODE_OFFSET()), 32) + + checkOverflow(dst,len, evmGasLeft) + checkOverflow(offset,len, evmGasLeft) + checkMemOverflow(add(dst, len), evmGasLeft) + // Check bytecode overflow + if gt(add(offset, len), sub(MEM_OFFSET(), 1)) { + revertWithGas(evmGasLeft) + } + + $llvm_AlwaysInline_llvm$_memcpy(dst, offset, len) + ip := add(ip, 1) + } + case 0x3A { // OP_GASPRICE + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, gasprice(), evmGasLeft) + ip := add(ip, 1) + } + case 0x3B { // OP_EXTCODESIZE + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + switch _isEVM(addr) + case 0 { sp := pushStackItemWithoutCheck(sp, extcodesize(addr)) } + default { sp := pushStackItemWithoutCheck(sp, _fetchDeployedCodeLen(addr)) } + ip := add(ip, 1) + } + case 0x3C { // OP_EXTCODECOPY + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) + ip := add(ip, 1) + } + case 0x3D { // OP_RETURNDATASIZE + evmGasLeft := chargeGas(evmGasLeft, 2) + + let rdz := mload(LAST_RETURNDATA_SIZE_OFFSET()) + sp := pushStackItem(sp, rdz, evmGasLeft) + ip := add(ip, 1) + } + case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + + let dest, offset, len + popStackCheck(sp, evmGasLeft, 3) + dest, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + len, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,len, evmGasLeft) + if gt(add(offset, len), mload(LAST_RETURNDATA_SIZE_OFFSET())) { + revertWithGas(evmGasLeft) + } + + // minimum_word_size = (size + 31) / 32 + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + checkMemOverflowByOffset(offset, evmGasLeft) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + ip := add(ip, 1) + } + case 0x3F { // OP_EXTCODEHASH + evmGasLeft := chargeGas(evmGasLeft, 100) + + let addr + addr, sp := popStackItem(sp, evmGasLeft) + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + evmGasLeft := chargeGas(evmGasLeft, 2500) + } + + ip := add(ip, 1) + if iszero(addr) { + sp := pushStackItemWithoutCheck(sp, 0) + continue + } + sp := pushStackItemWithoutCheck(sp, extcodehash(addr)) + } + case 0x40 { // OP_BLOCKHASH + evmGasLeft := chargeGas(evmGasLeft, 20) + + let blockNumber + popStackCheck(sp, evmGasLeft, 1) + blockNumber, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, blockhash(blockNumber)) + ip := add(ip, 1) + } + case 0x41 { // OP_COINBASE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, coinbase(), evmGasLeft) + ip := add(ip, 1) + } + case 0x42 { // OP_TIMESTAMP + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, timestamp(), evmGasLeft) + ip := add(ip, 1) + } + case 0x43 { // OP_NUMBER + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, number(), evmGasLeft) + ip := add(ip, 1) + } + case 0x44 { // OP_PREVRANDAO + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, prevrandao(), evmGasLeft) + ip := add(ip, 1) + } + case 0x45 { // OP_GASLIMIT + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, gaslimit(), evmGasLeft) + ip := add(ip, 1) + } + case 0x46 { // OP_CHAINID + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, chainid(), evmGasLeft) + ip := add(ip, 1) + } + case 0x47 { // OP_SELFBALANCE + evmGasLeft := chargeGas(evmGasLeft, 5) + sp := pushStackItem(sp, selfbalance(), evmGasLeft) + ip := add(ip, 1) + } + case 0x48 { // OP_BASEFEE + evmGasLeft := chargeGas(evmGasLeft, 2) + sp := pushStackItem(sp, basefee(), evmGasLeft) + ip := add(ip, 1) + } + case 0x50 { // OP_POP + evmGasLeft := chargeGas(evmGasLeft, 2) + + let _y + + _y, sp := popStackItem(sp, evmGasLeft) + ip := add(ip, 1) + } + case 0x51 { // OP_MLOAD + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset + + offset, sp := popStackItem(sp, evmGasLeft) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + let memValue := mload(add(MEM_OFFSET_INNER(), offset)) + sp := pushStackItemWithoutCheck(sp, memValue) + ip := add(ip, 1) + } + case 0x52 { // OP_MSTORE + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 32)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x53 { // OP_MSTORE8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + let offset, value + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + checkMemOverflowByOffset(offset, evmGasLeft) + let expansionGas := expandMemory(add(offset, 1)) + evmGasLeft := chargeGas(evmGasLeft, expansionGas) + + mstore8(add(MEM_OFFSET_INNER(), offset), value) + ip := add(ip, 1) + } + case 0x54 { // OP_SLOAD + + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key, value, isWarm + + key, sp := popStackItem(sp, evmGasLeft) + + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { + evmGasLeft := chargeGas(evmGasLeft, 2000) + } + + value := sload(key) + + if iszero(wasWarm) { + let _wasW, _orgV := warmSlot(key, value) + } + + sp := pushStackItemWithoutCheck(sp,value) + ip := add(ip, 1) + } + case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value, gasSpent + + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + ip := add(ip, 1) + { + // Here it is okay to read before we charge since we known anyway that + // the context has enough funds to compensate at least for the read. + // Im not sure if we need this before: require(gasLeft > GAS_CALL_STIPEND); + let currentValue := sload(key) + let wasWarm, originalValue := warmSlot(key, currentValue) + + if eq(value, currentValue) { + continue + } + + if eq(originalValue, currentValue) { + gasSpent := 19900 + if originalValue { + gasSpent := 2800 + } + } + + if iszero(wasWarm) { + gasSpent := add(gasSpent, 2100) + } + } + + evmGasLeft := chargeGas(evmGasLeft, gasSpent) + sstore(key, value) + + } + // NOTE: We don't currently do full jumpdest validation + // (i.e. validating a jumpdest isn't in PUSH data) + case 0x56 { // OP_JUMP + evmGasLeft := chargeGas(evmGasLeft, 8) + + let counter + + counter, sp := popStackItem(sp, evmGasLeft) + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x57 { // OP_JUMPI + evmGasLeft := chargeGas(evmGasLeft, 10) + + let counter, b + + popStackCheck(sp, evmGasLeft, 2) + counter, sp := popStackItemWithoutCheck(sp) + b, sp := popStackItemWithoutCheck(sp) + + if iszero(b) { + ip := add(ip, 1) + continue + } + + ip := add(add(BYTECODE_OFFSET(), 32), counter) + + // Check next opcode is JUMPDEST + let nextOpcode := readIP(ip,maxAcceptablePos) + if iszero(eq(nextOpcode, 0x5B)) { + revertWithGas(evmGasLeft) + } + + // execute JUMPDEST immediately + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x58 { // OP_PC + evmGasLeft := chargeGas(evmGasLeft, 2) + ip := add(ip, 1) + + // PC = ip - 32 (bytecode size) - 1 (current instruction) + sp := pushStackItem(sp, sub(sub(ip, BYTECODE_OFFSET()), 33), evmGasLeft) + } + case 0x59 { // OP_MSIZE + evmGasLeft := chargeGas(evmGasLeft,2) + + let size + + size := mload(MEM_OFFSET()) + size := shl(5,size) + sp := pushStackItem(sp,size, evmGasLeft) + ip := add(ip, 1) + } + case 0x5A { // OP_GAS + evmGasLeft := chargeGas(evmGasLeft, 2) + + sp := pushStackItem(sp, evmGasLeft, evmGasLeft) + ip := add(ip, 1) + } + case 0x5B { // OP_JUMPDEST + evmGasLeft := chargeGas(evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x5C { // OP_TLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) + + let key + popStackCheck(sp, evmGasLeft, 1) + key, sp := popStackItemWithoutCheck(sp) + + sp := pushStackItemWithoutCheck(sp, tload(key)) + ip := add(ip, 1) + } + case 0x5D { // OP_TSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let key, value + popStackCheck(sp, evmGasLeft, 2) + key, sp := popStackItemWithoutCheck(sp) + value, sp := popStackItemWithoutCheck(sp) + + tstore(key, value) + ip := add(ip, 1) + } + case 0x5E { // OP_MCOPY + let destOffset, offset, size + popStackCheck(sp, evmGasLeft, 3) + destOffset, sp := popStackItemWithoutCheck(sp) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkOverflow(destOffset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + checkMemOverflowByOffset(add(destOffset, size), evmGasLeft) + + expandMemory(add(destOffset, size)) + expandMemory(add(offset, size)) + + mcopy(add(destOffset, MEM_OFFSET_INNER()), add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0x5F { // OP_PUSH0 + evmGasLeft := chargeGas(evmGasLeft, 2) + + let value := 0 + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x60 { // OP_PUSH1 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,1) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 1) + } + case 0x61 { // OP_PUSH2 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,2) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 2) + } + case 0x62 { // OP_PUSH3 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,3) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 3) + } + case 0x63 { // OP_PUSH4 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,4) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 4) + } + case 0x64 { // OP_PUSH5 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,5) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 5) + } + case 0x65 { // OP_PUSH6 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,6) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 6) + } + case 0x66 { // OP_PUSH7 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,7) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 7) + } + case 0x67 { // OP_PUSH8 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,8) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 8) + } + case 0x68 { // OP_PUSH9 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,9) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 9) + } + case 0x69 { // OP_PUSH10 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,10) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 10) + } + case 0x6A { // OP_PUSH11 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,11) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 11) + } + case 0x6B { // OP_PUSH12 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,12) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 12) + } + case 0x6C { // OP_PUSH13 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,13) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 13) + } + case 0x6D { // OP_PUSH14 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,14) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 14) + } + case 0x6E { // OP_PUSH15 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,15) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 15) + } + case 0x6F { // OP_PUSH16 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,16) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 16) + } + case 0x70 { // OP_PUSH17 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,17) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 17) + } + case 0x71 { // OP_PUSH18 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,18) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 18) + } + case 0x72 { // OP_PUSH19 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,19) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 19) + } + case 0x73 { // OP_PUSH20 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,20) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 20) + } + case 0x74 { // OP_PUSH21 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,21) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 21) + } + case 0x75 { // OP_PUSH22 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,22) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 22) + } + case 0x76 { // OP_PUSH23 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,23) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 23) + } + case 0x77 { // OP_PUSH24 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,24) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 24) + } + case 0x78 { // OP_PUSH25 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,25) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 25) + } + case 0x79 { // OP_PUSH26 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,26) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 26) + } + case 0x7A { // OP_PUSH27 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,27) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 27) + } + case 0x7B { // OP_PUSH28 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,28) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 28) + } + case 0x7C { // OP_PUSH29 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,29) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 29) + } + case 0x7D { // OP_PUSH30 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,30) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 30) + } + case 0x7E { // OP_PUSH31 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,31) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 31) + } + case 0x7F { // OP_PUSH32 + evmGasLeft := chargeGas(evmGasLeft, 3) + + ip := add(ip, 1) + let value := readBytes(ip,maxAcceptablePos,32) + + sp := pushStackItem(sp, value, evmGasLeft) + ip := add(ip, 32) + } + case 0x80 { // OP_DUP1 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x81 { // OP_DUP2 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x82 { // OP_DUP3 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x83 { // OP_DUP4 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x84 { // OP_DUP5 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x85 { // OP_DUP6 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x86 { // OP_DUP7 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x87 { // OP_DUP8 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x88 { // OP_DUP9 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x89 { // OP_DUP10 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x8A { // OP_DUP11 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x8B { // OP_DUP12 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x8C { // OP_DUP13 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x8D { // OP_DUP14 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x8E { // OP_DUP15 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x8F { // OP_DUP16 + sp, evmGasLeft := dupStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0x90 { // OP_SWAP1 + evmGasLeft := swapStackItem(sp, evmGasLeft, 1) + ip := add(ip, 1) + } + case 0x91 { // OP_SWAP2 + evmGasLeft := swapStackItem(sp, evmGasLeft, 2) + ip := add(ip, 1) + } + case 0x92 { // OP_SWAP3 + evmGasLeft := swapStackItem(sp, evmGasLeft, 3) + ip := add(ip, 1) + } + case 0x93 { // OP_SWAP4 + evmGasLeft := swapStackItem(sp, evmGasLeft, 4) + ip := add(ip, 1) + } + case 0x94 { // OP_SWAP5 + evmGasLeft := swapStackItem(sp, evmGasLeft, 5) + ip := add(ip, 1) + } + case 0x95 { // OP_SWAP6 + evmGasLeft := swapStackItem(sp, evmGasLeft, 6) + ip := add(ip, 1) + } + case 0x96 { // OP_SWAP7 + evmGasLeft := swapStackItem(sp, evmGasLeft, 7) + ip := add(ip, 1) + } + case 0x97 { // OP_SWAP8 + evmGasLeft := swapStackItem(sp, evmGasLeft, 8) + ip := add(ip, 1) + } + case 0x98 { // OP_SWAP9 + evmGasLeft := swapStackItem(sp, evmGasLeft, 9) + ip := add(ip, 1) + } + case 0x99 { // OP_SWAP10 + evmGasLeft := swapStackItem(sp, evmGasLeft, 10) + ip := add(ip, 1) + } + case 0x9A { // OP_SWAP11 + evmGasLeft := swapStackItem(sp, evmGasLeft, 11) + ip := add(ip, 1) + } + case 0x9B { // OP_SWAP12 + evmGasLeft := swapStackItem(sp, evmGasLeft, 12) + ip := add(ip, 1) + } + case 0x9C { // OP_SWAP13 + evmGasLeft := swapStackItem(sp, evmGasLeft, 13) + ip := add(ip, 1) + } + case 0x9D { // OP_SWAP14 + evmGasLeft := swapStackItem(sp, evmGasLeft, 14) + ip := add(ip, 1) + } + case 0x9E { // OP_SWAP15 + evmGasLeft := swapStackItem(sp, evmGasLeft, 15) + ip := add(ip, 1) + } + case 0x9F { // OP_SWAP16 + evmGasLeft := swapStackItem(sp, evmGasLeft, 16) + ip := add(ip, 1) + } + case 0xA0 { // OP_LOG0 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + log0(add(offset, MEM_OFFSET_INNER()), size) + ip := add(ip, 1) + } + case 0xA1 { // OP_LOG1 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 3) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 375) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1 + topic1, sp := popStackItemWithoutCheck(sp) + log1(add(offset, MEM_OFFSET_INNER()), size, topic1) + } + ip := add(ip, 1) + } + case 0xA2 { // OP_LOG2 + evmGasLeft := chargeGas(evmGasLeft, 375) + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 4) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 750) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } + ip := add(ip, 1) + } + case 0xA3 { // OP_LOG3 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 5) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1125) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) + } + ip := add(ip, 1) + } + case 0xA4 { // OP_LOG4 + evmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + revertWithGas(evmGasLeft) + } + + let offset, size + popStackCheck(sp, evmGasLeft, 6) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset, size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(add(offset, size))) + dynamicGas := add(dynamicGas, 1500) + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + { + let topic1, topic2, topic3, topic4 + topic1, sp := popStackItemWithoutCheck(sp) + topic2, sp := popStackItemWithoutCheck(sp) + topic3, sp := popStackItemWithoutCheck(sp) + topic4, sp := popStackItemWithoutCheck(sp) + log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) + } + ip := add(ip, 1) + } + case 0xF0 { // OP_CREATE + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) + ip := add(ip, 1) + } + case 0xF1 { // OP_CALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + + // A function was implemented in order to avoid stack depth errors. + gasUsed, sp := performCall(sp, evmGasLeft, isStatic) + + // Check if the following is ok + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF3 { // OP_RETURN + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset,size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET_INNER(), offset) + break + } + case 0xF4 { // OP_DELEGATECALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + sp, isStatic, gasUsed := delegateCall(sp, isStatic, evmGasLeft) + + evmGasLeft := chargeGas(evmGasLeft, gasUsed) + ip := add(ip, 1) + } + case 0xF5 { // OP_CREATE2 + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0, evmGasLeft) } + default { sp := pushStackItem(sp, addr, evmGasLeft) } + ip := add(ip, 1) + } + case 0xFA { // OP_STATICCALL + evmGasLeft := chargeGas(evmGasLeft, 100) + + let gasUsed + gasUsed, sp := performStaticCall(sp,evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,gasUsed) + ip := add(ip, 1) + } + case 0xFD { // OP_REVERT + let offset,size + + popStackCheck(sp, evmGasLeft, 2) + offset, sp := popStackItemWithoutCheck(sp) + size, sp := popStackItemWithoutCheck(sp) + + checkOverflow(offset,size, evmGasLeft) + checkMemOverflowByOffset(add(offset, size), evmGasLeft) + evmGasLeft := chargeGas(evmGasLeft,expandMemory(add(offset,size))) + + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET_INNER()) + offset,size := addGasIfEvmRevert(isCallerEVM,offset,size,evmGasLeft) + + revert(offset,size) + } + case 0xFE { // OP_INVALID + evmGasLeft := 0 + + revertWithGas(evmGasLeft) + } + default { + printString("INVALID OPCODE") + printHex(opcode) + revert(0, 0) + } +} diff --git a/system-contracts/package.json b/system-contracts/package.json index ef53db110..1a04c8632 100644 --- a/system-contracts/package.json +++ b/system-contracts/package.json @@ -63,6 +63,7 @@ "compile-zasm": "ts-node scripts/compile-zasm.ts", "deploy-preimages": "ts-node scripts/deploy-preimages.ts", "preprocess:bootloader": "rm -rf ./bootloader/build && yarn ts-node scripts/preprocess-bootloader.ts", + "preprocess:interpreter": "rm -f ./contracts/EvmInterpreter.yul && yarn ts-node scripts/preprocess-interpreter.ts", "preprocess:system-contracts": "rm -rf ./contracts-preprocessed && ts-node scripts/preprocess-system-contracts.ts", "verify-on-explorer": "hardhat run scripts/verify-on-explorer.ts", "test": "yarn build:test-system-contracts && hardhat test --network zkSyncTestNode", diff --git a/system-contracts/scripts/compile-yul.ts b/system-contracts/scripts/compile-yul.ts index 67b468987..e8d91622b 100644 --- a/system-contracts/scripts/compile-yul.ts +++ b/system-contracts/scripts/compile-yul.ts @@ -12,7 +12,7 @@ import * as fs from "fs"; import { Command } from "commander"; import * as _path from "path"; -const COMPILER_VERSION = "1.3.18"; +const COMPILER_VERSION = "1.5.0"; const IS_COMPILER_PRE_RELEASE = true; const CONTRACTS_DIR = "contracts-preprocessed"; const BOOTLOADER_DIR = "bootloader"; diff --git a/system-contracts/scripts/deploy-preimages.ts b/system-contracts/scripts/deploy-preimages.ts index 0029f56a0..afff747cb 100644 --- a/system-contracts/scripts/deploy-preimages.ts +++ b/system-contracts/scripts/deploy-preimages.ts @@ -23,6 +23,7 @@ const MAX_COMBINED_LENGTH = 125000; const DEFAULT_ACCOUNT_CONTRACT_NAME = "DefaultAccount"; const BOOTLOADER_CONTRACT_NAME = "Bootloader"; +const EVM_SIMULATOR_CONTRACT_NAME = "EvmInterpreter"; const CONSOLE_COLOR_RESET = "\x1b[0m"; const CONSOLE_COLOR_RED = "\x1b[31m"; @@ -157,6 +158,55 @@ class ZkSyncDeployer { return await zkSync.getL2BootloaderBytecodeHash(); } + // Returns the current default evm simulator bytecode on zkSync + async currentEvmSimulatorBytecode(): Promise { + const zkSync = await this.deployer.zkWallet.getMainContract(); + return await zkSync.getL2EvmSimulatorBytecodeHash(); + } + + // If needed, appends the evm simulator bytecode to the upgrade + async checkShouldUpgradeEvmSimulator(evmSimulatorBytecode: string) { + const bytecodeHash = ethers.utils.hexlify(hashBytecode(evmSimulatorBytecode)); + const currentEvmSimulatorBytecode = ethers.utils.hexlify(await this.currentEvmSimulatorBytecode()); + + // If the bytecode is not the same as the one deployed on zkSync, we need to add it to the deployment + if (bytecodeHash.toLowerCase() !== currentEvmSimulatorBytecode) { + this.defaultAccountToUpgrade = { + name: EVM_SIMULATOR_CONTRACT_NAME, + bytecodeHashes: [bytecodeHash], + }; + } + } + + // Publishes the bytecode of the evm simulator and appends it to the deployed bytecodes if needed. + async processEvmSimulator() { + const defaultEvmSimulator = (await this.deployer.loadArtifact(EVM_SIMULATOR_CONTRACT_NAME)).bytecode; + + await this.publishEvmSimulator(defaultEvmSimulator); + await this.checkShouldUpgradeEvmSimulator(defaultEvmSimulator); + } + + async publishEvmSimulator(defaultEvmSimulatorBytecode: string) { + const [defaultEvmSimulatorBytecodes] = await filterPublishedFactoryDeps( + EVM_SIMULATOR_CONTRACT_NAME, + [defaultEvmSimulatorBytecode], + this.deployer + ); + + if (defaultEvmSimulatorBytecodes.length == 0) { + console.log("Default evm simulator is already published, skipping"); + return; + } + + // Publish evm simulator bytecode + await this.publishFactoryDeps([ + { + name: EVM_SIMULATOR_CONTRACT_NAME, + bytecodes: defaultEvmSimulatorBytecodes, + }, + ]); + } + async checkShouldUpgradeBootloader(bootloaderCode: string) { const bytecodeHash = ethers.utils.hexlify(hashBytecode(bootloaderCode)); const currentBootloaderBytecode = ethers.utils.hexlify(await this.currentBootloaderBytecode()); @@ -302,6 +352,7 @@ async function main() { .option("--l2Rpc ") .option("--bootloader") .option("--default-aa") + .option("--evm-simulator") .option("--system-contracts") .option("--file ") .action(async (cmd) => { @@ -340,6 +391,10 @@ async function main() { await zkSyncDeployer.processDefaultAA(); } + if (cmd.evmSimulator) { + await zkSyncDeployer.processEvmSimulator(); + } + if (cmd.systemContracts) { const dependenciesToPublish = await zkSyncDeployer.prepareContractsForPublishing(); await zkSyncDeployer.publishDependencies(dependenciesToPublish); diff --git a/system-contracts/scripts/preprocess-interpreter.ts b/system-contracts/scripts/preprocess-interpreter.ts new file mode 100644 index 000000000..2b031ab9c --- /dev/null +++ b/system-contracts/scripts/preprocess-interpreter.ts @@ -0,0 +1,22 @@ +import { writeFileSync, readFileSync } from "fs"; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const preprocess = require("preprocess"); +/* eslint-enable@typescript-eslint/no-var-requires */ + +const OUTPUT_DIR = "contracts"; +const INPUT_DIR = "evm-interpreter"; + +async function main() { + process.chdir(`${INPUT_DIR}`); + const interpreterSource = readFileSync("EvmInterpreter.template.yul").toString(); + + console.log("Preprocessing Interpreter"); + const interpreter = preprocess.preprocess(interpreterSource); + + writeFileSync(`../${OUTPUT_DIR}/EvmInterpreter.yul`, interpreter); + + console.log("Interpreter preprocessing done!"); +} + +main();