From c25b5d9cfa2a58cd12b03911fb243c289ceba879 Mon Sep 17 00:00:00 2001 From: Thomas Nguy Date: Fri, 28 Jun 2024 22:38:22 +0900 Subject: [PATCH] fix bootloader --- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 76320 -> 76256 bytes .../gas_test.yul/gas_test.yul.zbin | Bin 72416 -> 72352 bytes .../playground_batch.yul.zbin | Bin 76512 -> 76448 bytes .../proved_batch.yul/proved_batch.yul | 3971 +++++++++++++++++ .../proved_batch.yul/proved_batch.yul.zbin | Bin 72928 -> 0 bytes 5 files changed, 3971 insertions(+) create mode 100644 etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul delete mode 100644 etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul.zbin diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin index 75ee6377bdb645532c55f06f0e883cc078b5bd2b..a864fc8a3c32caabd9022702454ce886f807476c 100644 GIT binary patch delta 15704 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 XKK<0I-DAEUy!y4@ufE|*?TP;b!+~Mg delta 15232 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 ZM5&Ulyb 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 diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin index b1a5e063d84b66ebea9a6e021ef42d29b1346760..f3cdb6e3dfc0dacf8e8dfab2e7b60e762dc7b4ec 100644 GIT binary patch delta 15896 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 ret { + // 2^128 - 1 + ret := 340282366920938463463374607431768211455 + } + + function MAX_ALLOWED_FAIR_L2_GAS_PRICE() -> ret { + // 2^128 - 1 + ret := 340282366920938463463374607431768211455 + } + + /// @dev This method ensures that the prices provided by the operator + /// are not absurdly high + function validateOperatorProvidedPrices(fairL2GasPrice, pubdataPrice) { + // The limit is the same for pubdata price and L1 gas price + if gt(pubdataPrice, MAX_ALLOWED_FAIR_PUBDATA_PRICE()) { + assertionError("Fair pubdata price too high") + } + + if gt(fairL2GasPrice, MAX_ALLOWED_FAIR_L2_GAS_PRICE()) { + assertionError("L2 fair gas price too high") + } + } + + /// @dev The overhead for a transaction slot in L2 gas. + /// It is roughly equal to 80kk/MAX_TRANSACTIONS_IN_BATCH, i.e. how many gas would an L1->L2 transaction + /// need to pay to compensate for the batch being closed. + /// @dev It is expected of the operator to set the "fair L2 gas price" appropriately to ensure that it is + /// compensated enough in case the batch might be prematurely sealed because of the transaction slots being filled up. + function TX_SLOT_OVERHEAD_GAS() -> ret { + ret := 10000 + } + + /// @dev The overhead for each byte of the bootloader memory that the encoding of the transaction. + /// It is roughly equal to 80kk/BOOTLOADER_MEMORY_FOR_TXS, i.e. how many gas would an L1->L2 transaction + /// need to pay to compensate for the batch being closed. + /// @dev It is expected of the operator to set the "fair L2 gas price" appropriately to ensure that it is + /// compensated enough in case the batch might be prematurely sealed because of the memory being filled up. + function MEMORY_OVERHEAD_GAS() -> ret { + ret := 10 + } + + /// @dev Returns the base fee and gas per pubdata based on the fair pubdata price and L2 gas price provided by the operator + /// @param pubdataPrice The price of a single byte of pubdata in Wei + /// @param fairL2GasPrice The price of an L2 gas in Wei + /// @return baseFee and gasPerPubdata The base fee and the gas per pubdata to be used by L2 transactions in this batch. + function getFeeParams( + fairPubdataPrice, + fairL2GasPrice, + ) -> baseFee, gasPerPubdata { + baseFee := max( + fairL2GasPrice, + ceilDiv(fairPubdataPrice, MAX_L2_GAS_PER_PUBDATA()) + ) + + gasPerPubdata := gasPerPubdataFromBaseFee(baseFee, fairPubdataPrice) + } + + /// @dev Calculates the gas per pubdata based on the pubdata price provided by the operator + /// as well the the fixed baseFee. + function gasPerPubdataFromBaseFee(baseFee, pubdataPrice) -> ret { + ret := ceilDiv(pubdataPrice, baseFee) + } + + /// @dev It should be always possible to submit a transaction + /// that consumes such amount of public data. + function GUARANTEED_PUBDATA_PER_TX() -> ret { + ret := 2500 + } + + /// @dev The maximal allowed gasPerPubdata, we want it multiplied by the u32::MAX + /// (i.e. the maximal possible value of the pubdata counter) to be a safe JS integer with a good enough margin. + /// @dev For now, the 50000 value is used for backward compatibility with SDK, but in the future we will migrate to 2^20. + function MAX_L2_GAS_PER_PUBDATA() -> ret { + ret := 50000 + } + + /// @dev The overhead for the interaction with L1. + /// It should cover proof verification as well as other minor + /// overheads for committing/executing a transaction in a batch. + function BATCH_OVERHEAD_L1_GAS() -> ret { + ret := 1000000 + } + + /// @dev The maximal number of gas available to the transaction + function MAX_GAS_PER_TRANSACTION() -> ret { + ret := 80000000 + } + + /// @dev The number of L1 gas needed to be spent for + /// L1 byte. While a single pubdata byte costs `16` gas, + /// we demand at least 17 to cover up for the costs of additional + /// hashing of it, etc. + function L1_GAS_PER_PUBDATA_BYTE() -> ret { + ret := 17 + } + + /// @dev Whether the batch is allowed to accept transactions with + /// gasPerPubdataByteLimit = 0. On mainnet, this is forbidden for safety reasons. + function FORBID_ZERO_GAS_PER_PUBDATA() -> ret { + ret := 1 + } + + /// @dev The maximum number of transactions per L1 batch. + function MAX_TRANSACTIONS_IN_BATCH() -> ret { + ret := 10000 + } + + /// @dev The slot from which the scratch space starts. + /// Scratch space is used for various temporary values + function SCRATCH_SPACE_BEGIN_SLOT() -> ret { + ret := 8 + } + + /// @dev The byte from which the scratch space starts. + /// Scratch space is used for various temporary values + function SCRATCH_SPACE_BEGIN_BYTE() -> ret { + ret := mul(SCRATCH_SPACE_BEGIN_SLOT(), 32) + } + + /// @dev The first 32 slots are reserved for event emitting for the + /// debugging purposes + function SCRATCH_SPACE_SLOTS() -> ret { + ret := 32 + } + + /// @dev Slots reserved for saving the paymaster context + /// @dev The paymasters are allowed to consume at most + /// 32 slots (1024 bytes) for their context. + /// The 33 slots are required since the first one stores the length of the calldata. + function PAYMASTER_CONTEXT_SLOTS() -> ret { + ret := 33 + } + + /// @dev Bytes reserved for saving the paymaster context + function PAYMASTER_CONTEXT_BYTES() -> ret { + ret := mul(PAYMASTER_CONTEXT_SLOTS(), 32) + } + + /// @dev Slot from which the paymaster context starts + function PAYMASTER_CONTEXT_BEGIN_SLOT() -> ret { + ret := add(SCRATCH_SPACE_BEGIN_SLOT(), SCRATCH_SPACE_SLOTS()) + } + + /// @dev The byte from which the paymaster context starts + function PAYMASTER_CONTEXT_BEGIN_BYTE() -> ret { + ret := mul(PAYMASTER_CONTEXT_BEGIN_SLOT(), 32) + } + + /// @dev Each tx must have at least this amount of unused bytes before them to be able to + /// encode the postOp operation correctly. + function MAX_POSTOP_SLOTS() -> ret { + // Before the actual transaction encoding, the postOp contains 6 slots: + // 1. Context offset + // 2. Transaction offset + // 3. Transaction hash + // 4. Suggested signed hash + // 5. Transaction result + // 6. Maximum refunded gas + // And one more slot for the padding selector + ret := add(PAYMASTER_CONTEXT_SLOTS(), 7) + } + + /// @dev Slots needed to store the canonical and signed hash for the current L2 transaction. + function CURRENT_L2_TX_HASHES_RESERVED_SLOTS() -> ret { + ret := 2 + } + + /// @dev Slot from which storing of the current canonical and signed hashes begins + function CURRENT_L2_TX_HASHES_BEGIN_SLOT() -> ret { + ret := add(PAYMASTER_CONTEXT_BEGIN_SLOT(), PAYMASTER_CONTEXT_SLOTS()) + } + + /// @dev The byte from which storing of the current canonical and signed hashes begins + function CURRENT_L2_TX_HASHES_BEGIN_BYTE() -> ret { + ret := mul(CURRENT_L2_TX_HASHES_BEGIN_SLOT(), 32) + } + + /// @dev The maximum number of new factory deps that are allowed in a transaction + function MAX_NEW_FACTORY_DEPS() -> ret { + ret := 32 + } + + /// @dev Besides the factory deps themselves, we also need another 4 slots for: + /// selector, marker of whether the user should pay for the pubdata, + /// the offset for the encoding of the array as well as the length of the array. + function NEW_FACTORY_DEPS_RESERVED_SLOTS() -> ret { + ret := add(MAX_NEW_FACTORY_DEPS(), 4) + } + + /// @dev The slot starting from which the factory dependencies are stored + function NEW_FACTORY_DEPS_BEGIN_SLOT() -> ret { + ret := add(CURRENT_L2_TX_HASHES_BEGIN_SLOT(), CURRENT_L2_TX_HASHES_RESERVED_SLOTS()) + } + + /// @dev The byte starting from which the factory dependencies are stored + function NEW_FACTORY_DEPS_BEGIN_BYTE() -> ret { + ret := mul(NEW_FACTORY_DEPS_BEGIN_SLOT(), 32) + } + + /// @dev The slot starting from which the refunds provided by the operator are stored + function TX_OPERATOR_REFUND_BEGIN_SLOT() -> ret { + ret := add(NEW_FACTORY_DEPS_BEGIN_SLOT(), NEW_FACTORY_DEPS_RESERVED_SLOTS()) + } + + /// @dev The byte starting from which the refunds provided by the operator are stored + function TX_OPERATOR_REFUND_BEGIN_BYTE() -> ret { + ret := mul(TX_OPERATOR_REFUND_BEGIN_SLOT(), 32) + } + + /// @dev The number of slots dedicated for the refunds for the transactions. + /// It is equal to the number of transactions in the batch. + function TX_OPERATOR_REFUNDS_SLOTS() -> ret { + ret := MAX_TRANSACTIONS_IN_BATCH() + } + + /// @dev The slot starting from which the overheads proposed by the operator will be stored + function TX_SUGGESTED_OVERHEAD_BEGIN_SLOT() -> ret { + ret := add(TX_OPERATOR_REFUND_BEGIN_SLOT(), TX_OPERATOR_REFUNDS_SLOTS()) + } + + /// @dev The byte starting from which the overheads proposed by the operator will be stored + function TX_SUGGESTED_OVERHEAD_BEGIN_BYTE() -> ret { + ret := mul(TX_SUGGESTED_OVERHEAD_BEGIN_SLOT(), 32) + } + + /// @dev The number of slots dedicated for the overheads for the transactions. + /// It is equal to the number of transactions in the batch. + function TX_SUGGESTED_OVERHEAD_SLOTS() -> ret { + ret := MAX_TRANSACTIONS_IN_BATCH() + } + + /// @dev The slot starting from which the maximum number of gas that the operator "trusts" + /// the transaction to use for its execution is stored. Sometimes, the operator may know that + /// a certain transaction can be allowed more gas that what the protocol-level worst-case allows. + function TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT() -> ret { + ret := add(TX_SUGGESTED_OVERHEAD_BEGIN_SLOT(), TX_SUGGESTED_OVERHEAD_SLOTS()) + } + + /// @dev byte starting from which the maximum number of gas that the operator "trusts" + /// the transaction to use for its execution is stored. + function TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_BYTE() -> ret { + ret := mul(TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT(), 32) + } + + /// @dev The number of slots dedicated for the trusted gas limits for the transactions. + /// It is equal to the number of transactions in the batch. + function TX_OPERATOR_TRUSTED_GAS_LIMIT_SLOTS() -> ret { + ret := MAX_TRANSACTIONS_IN_BATCH() + } + + /// @dev The slot starting from the L2 block information for transactions is stored. + function TX_OPERATOR_L2_BLOCK_INFO_BEGIN_SLOT() -> ret { + ret := add(TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT(), TX_OPERATOR_TRUSTED_GAS_LIMIT_SLOTS()) + } + + /// @dev The byte starting from which the L2 block information for transactions is stored. + function TX_OPERATOR_L2_BLOCK_INFO_BEGIN_BYTE() -> ret { + ret := mul(TX_OPERATOR_L2_BLOCK_INFO_BEGIN_SLOT(), 32) + } + + /// @dev The size of each of the L2 block information. Each L2 block information contains four fields: + /// - number of the block + /// - timestamp of the block + /// - hash of the previous block + /// - the maximal number of virtual blocks to create + function TX_OPERATOR_L2_BLOCK_INFO_SLOT_SIZE() -> ret { + ret := 4 + } + + /// @dev The size of each of the L2 block information in bytes. + function TX_OPERATOR_L2_BLOCK_INFO_SIZE_BYTES() -> ret { + ret := mul(TX_OPERATOR_L2_BLOCK_INFO_SLOT_SIZE(), 32) + } + + /// @dev The number of slots dedicated for the L2 block information for the transactions. + /// Note, that an additional slot is required for the fictive L2 block at the end of the batch. + /// For technical reasons inside the sequencer implementation, + /// each batch ends with a fictive block with no transactions. + function TX_OPERATOR_L2_BLOCK_INFO_SLOTS() -> ret { + ret := mul(add(MAX_TRANSACTIONS_IN_BATCH(), 1), TX_OPERATOR_L2_BLOCK_INFO_SLOT_SIZE()) + } + + /// @dev The slot starting from which the compressed bytecodes are located in the bootloader's memory. + /// Each compressed bytecode is provided in the following format: + /// - 32 byte formatted bytecode hash + /// - 32 byte of zero (it will be replaced within the code with left-padded selector of the `publishCompressedBytecode`). + /// - ABI-encoding of the parameters of the `publishCompressedBytecode` method. + /// + /// At the slot `TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT()` the pointer to the currently processed compressed bytecode + /// is stored, i.e. this pointer will be increased once the current bytecode which the pointer points to is published. + /// At the start of the bootloader, the value stored at the `TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT` is equal to + /// `TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_SLOT + 32`, where the hash of the first compressed bytecode to publish should be stored. + function COMPRESSED_BYTECODES_BEGIN_SLOT() -> ret { + ret := add(TX_OPERATOR_L2_BLOCK_INFO_BEGIN_SLOT(), TX_OPERATOR_L2_BLOCK_INFO_SLOTS()) + } + + /// @dev The byte starting from which the compressed bytecodes are located in the bootloader's memory. + function COMPRESSED_BYTECODES_BEGIN_BYTE() -> ret { + ret := mul(COMPRESSED_BYTECODES_BEGIN_SLOT(), 32) + } + + /// @dev The number of slots dedicated to the compressed bytecodes. + function COMPRESSED_BYTECODES_SLOTS() -> ret { + ret := 196608 + } + + /// @dev The slot right after the last slot of the compressed bytecodes memory area. + function COMPRESSED_BYTECODES_END_SLOT() -> ret { + ret := add(COMPRESSED_BYTECODES_BEGIN_SLOT(), COMPRESSED_BYTECODES_SLOTS()) + } + + /// @dev The first byte in memory right after the compressed bytecodes memory area. + function COMPRESSED_BYTECODES_END_BYTE() -> ret { + ret := mul(COMPRESSED_BYTECODES_END_SLOT(), 32) + } + + /// @dev Slots needed to store priority txs L1 data (`chainedPriorityTxsHash` and `numberOfLayer1Txs`). + function PRIORITY_TXS_L1_DATA_RESERVED_SLOTS() -> ret { + ret := 2 + } + + /// @dev Slot from which storing of the priority txs L1 data begins. + function PRIORITY_TXS_L1_DATA_BEGIN_SLOT() -> ret { + ret := add(COMPRESSED_BYTECODES_BEGIN_SLOT(), COMPRESSED_BYTECODES_SLOTS()) + } + + /// @dev The byte from which storing of the priority txs L1 data begins. + function PRIORITY_TXS_L1_DATA_BEGIN_BYTE() -> ret { + ret := mul(PRIORITY_TXS_L1_DATA_BEGIN_SLOT(), 32) + } + + /// @dev Slot from which storing of the L1 Messenger pubdata begins. + function OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_BEGIN_SLOT() -> ret { + ret := add(PRIORITY_TXS_L1_DATA_BEGIN_SLOT(), PRIORITY_TXS_L1_DATA_RESERVED_SLOTS()) + } + + /// @dev The byte storing of the L1 Messenger pubdata begins. + function OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_BEGIN_BYTE() -> ret { + ret := mul(OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_BEGIN_SLOT(), 32) + } + + /// @dev Slots needed to store L1 Messenger pubdata. + /// @dev Note that are many more these than the maximal pubdata in batch, since + /// it needs to also accommodate uncompressed state diffs that are required for the state diff + /// compression verification. + function OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS() -> ret { + ret := 1360000 + } + + /// @dev The slot right after the last slot of the L1 Messenger pubdata memory area. + function OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_END_SLOT() -> ret { + ret := add(OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_BEGIN_SLOT(), OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS()) + } + + /// @dev The slot from which the bootloader transactions' descriptions begin + function TX_DESCRIPTION_BEGIN_SLOT() -> ret { + ret := OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_END_SLOT() + } + + /// @dev The byte from which the bootloader transactions' descriptions begin + function TX_DESCRIPTION_BEGIN_BYTE() -> ret { + ret := mul(TX_DESCRIPTION_BEGIN_SLOT(), 32) + } + + // Each tx description has the following structure + // + // struct BootloaderTxDescription { + // uint256 txMeta; + // uint256 txDataOffset; + // } + // + // `txMeta` contains flags to manipulate the transaction execution flow. + // For playground batches: + // It can have the following information (0 byte is LSB and 31 byte is MSB): + // 0 byte: `execute`, bool. Denotes whether transaction should be executed by the bootloader. + // 31 byte: server-side tx execution mode + // For proved batches: + // It can simply denotes whether to execute the transaction (0 to stop executing the batch, 1 to continue) + // + // Each such encoded struct consumes 2 words + function TX_DESCRIPTION_SIZE() -> ret { + ret := 64 + } + + /// @dev The byte right after the basic description of bootloader transactions + function TXS_IN_BATCH_LAST_PTR() -> ret { + ret := add(TX_DESCRIPTION_BEGIN_BYTE(), mul(MAX_TRANSACTIONS_IN_BATCH(), TX_DESCRIPTION_SIZE())) + } + + /// @dev The memory page consists of 59000000 / 32 VM words. + /// Each execution result is a single boolean, but + /// for the sake of simplicity we will spend 32 bytes on each + /// of those for now. + function MAX_MEM_SIZE() -> ret { + ret := 63800000 + } + + function L1_TX_INTRINSIC_L2_GAS() -> ret { + ret := 167157 + } + + function L1_TX_INTRINSIC_PUBDATA() -> ret { + ret := 88 + } + + function L2_TX_INTRINSIC_GAS() -> ret { + ret := 14070 + } + + function L2_TX_INTRINSIC_PUBDATA() -> ret { + ret := 0 + } + + /// @dev The byte from which the pointers on the result of transactions are stored + function RESULT_START_PTR() -> ret { + ret := sub(MAX_MEM_SIZE(), mul(MAX_TRANSACTIONS_IN_BATCH(), 32)) + } + + /// @dev The pointer writing to which invokes the VM hooks + function VM_HOOK_PTR() -> ret { + ret := sub(RESULT_START_PTR(), 32) + } + + /// @dev The maximum number the VM hooks may accept + function VM_HOOK_PARAMS() -> ret { + ret := 3 + } + + /// @dev The offset starting from which the parameters for VM hooks are located + function VM_HOOK_PARAMS_OFFSET() -> ret { + ret := sub(VM_HOOK_PTR(), mul(VM_HOOK_PARAMS(), 32)) + } + + function LAST_FREE_SLOT() -> ret { + // The slot right before the vm hooks is the last slot that + // can be used for transaction's descriptions + ret := sub(VM_HOOK_PARAMS_OFFSET(), 32) + } + + /// @dev The formal address of the bootloader + function BOOTLOADER_FORMAL_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008001 + } + + function ACCOUNT_CODE_STORAGE_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008002 + } + + function NONCE_HOLDER_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008003 + } + + function KNOWN_CODES_CONTRACT_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008004 + } + + function CONTRACT_DEPLOYER_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008006 + } + + function FORCE_DEPLOYER() -> ret { + ret := 0x0000000000000000000000000000000000008007 + } + + function L1_MESSENGER_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008008 + } + + function MSG_VALUE_SIMULATOR_ADDR() -> ret { + ret := 0x0000000000000000000000000000000000008009 + } + + function ETH_L2_TOKEN_ADDR() -> ret { + ret := 0x000000000000000000000000000000000000800a + } + + function SYSTEM_CONTEXT_ADDR() -> ret { + ret := 0x000000000000000000000000000000000000800b + } + + function BOOTLOADER_UTILITIES() -> ret { + ret := 0x000000000000000000000000000000000000800c + } + + function BYTECODE_COMPRESSOR_ADDR() -> ret { + ret := 0x000000000000000000000000000000000000800e + } + + function MAX_SYSTEM_CONTRACT_ADDR() -> ret { + ret := 0x000000000000000000000000000000000000ffff + } + + /// @dev The minimal allowed distance in bytes between the pointer to the compressed data + /// and the end of the area dedicated for the compressed bytecodes. + /// In fact, only distance of 192 should be sufficient: there it would be possible to insert + /// the hash of the bytecode, the 32 bytes buffer for selector and 2 offsets of the calldata, + /// but we keep it at 512 just in case. + function MIN_ALLOWED_OFFSET_FOR_COMPRESSED_BYTES_POINTER() -> ret { + ret := 512 + } + + /// @dev Whether the bootloader should enforce that accounts have returned the correct + /// magic value for signature. This value is enforced to be "true" on the main proved batch, but + /// we need the ability to ignore invalid signature results during fee estimation, + /// where the signature for the transaction is usually not known beforehand. + function SHOULD_ENSURE_CORRECT_RETURNED_MAGIC() -> ret { + ret := 1 + } + + /// @notice The type of the transaction used for system upgrades. + function UPGRADE_TRANSACTION_TX_TYPE() -> ret { + ret := 254 + } + + /// @notice The type of every non-upgrade transaction that comes from L1. + function L1_TX_TYPE() -> ret { + ret := 255 + } + + /// @dev The overhead in gas that will be used when checking whether the context has enough gas, i.e. + /// when checking for X gas, the context should have at least X+CHECK_ENOUGH_GAS_OVERHEAD() gas. + function CHECK_ENOUGH_GAS_OVERHEAD() -> ret { + ret := 1000000 + } + + /// @dev Ceil division of integers + function ceilDiv(x, y) -> ret { + switch or(eq(x, 0), eq(y, 0)) + case 0 { + // (x + y - 1) / y can overflow on addition, so we distribute. + ret := add(div(sub(x, 1), y), 1) + } + default { + ret := 0 + } + } + + /// @dev Calculates the length of a given number of bytes rounded up to the nearest multiple of 32. + function lengthRoundedByWords(len) -> ret { + let neededWords := div(add(len, 31), 32) + ret := safeMul(neededWords, 32, "xv") + } + + /// @dev Function responsible for processing the transaction + /// @param txDataOffset The offset to the ABI-encoding of the structure + /// @param resultPtr The pointer at which the result of the transaction's execution should be stored + /// @param transactionIndex The index of the transaction in the batch + /// @param isETHCall Whether the call is an ethCall. + /// @param gasPerPubdata The number of L2 gas to charge users for each byte of pubdata + /// On proved batch this value should always be zero + function processTx( + txDataOffset, + resultPtr, + transactionIndex, + isETHCall, + gasPerPubdata + ) { + // We set the L2 block info for this particular transaction + setL2Block(transactionIndex) + + let innerTxDataOffset := add(txDataOffset, 32) + + // By default we assume that the transaction has failed. + mstore(resultPtr, 0) + + let userProvidedPubdataPrice := getGasPerPubdataByteLimit(innerTxDataOffset) + debugLog("userProvidedPubdataPrice:", userProvidedPubdataPrice) + + debugLog("gasPerPubdata:", gasPerPubdata) + + switch getTxType(innerTxDataOffset) + case 254 { + // This is an upgrade transaction. + // Protocol upgrade transactions are processed totally in the same manner as the normal L1->L2 transactions, + // the only difference are: + // - They must be the first one in the batch + // - They have a different type to prevent tx hash collisions and preserve the expectation that the + // L1->L2 transactions have priorityTxId inside them. + if transactionIndex { + assertionError("Protocol upgrade tx not first") + } + + // This is to be called in the event that the L1 Transaction is a protocol upgrade txn. + // Since this is upgrade transactions, we are okay that the gasUsed by the transaction will + // not cover this additional hash computation + let canonicalL1TxHash := getCanonicalL1TxHash(txDataOffset) + sendToL1Native(true, protocolUpgradeTxHashKey(), canonicalL1TxHash) + + processL1Tx(txDataOffset, resultPtr, transactionIndex, userProvidedPubdataPrice, false) + } + case 255 { + // This is an L1->L2 transaction. + processL1Tx(txDataOffset, resultPtr, transactionIndex, userProvidedPubdataPrice, true) + } + default { + // The user has not agreed to this pubdata price + if lt(userProvidedPubdataPrice, gasPerPubdata) { + revertWithReason(UNACCEPTABLE_GAS_PRICE_ERR_CODE(), 0) + } + + processL2Tx(txDataOffset, resultPtr, transactionIndex, gasPerPubdata) + + } + } + + /// @notice Returns "raw" code hash of the address. "Raw" means that it returns exactly the value + /// that is stored in the AccountCodeStorage system contract for that address, without applying any + /// additional transformations, which the standard `extcodehash` does for EVM-compatibility + /// @param addr The address of the account to get the code hash of. + /// @param assertSuccess Whether to revert the bootloader if the call to the AccountCodeStorage fails. If `false`, only + /// `nearCallPanic` will be issued in case of failure, which is helpful for cases, when the reason for failure is user providing not + /// enough gas. + function getRawCodeHash(addr, assertSuccess) -> ret { + mstore(0, 0x4de2e46800000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + let success := staticcall( + gas(), + ACCOUNT_CODE_STORAGE_ADDR(), + 0, + 36, + 0, + 32 + ) + + // In case the call to the account code storage fails, + // it most likely means that the caller did not provide enough gas for + // the call. + // In case the caller is certain that the amount of gas provided is enough, i.e. + // (`assertSuccess` = true), then we should panic. + if iszero(success) { + if assertSuccess { + // The call must've succeeded, but it didn't. So we revert the bootloader. + assertionError("getRawCodeHash failed") + } + + // Most likely not enough gas provided, revert the current frame. + nearCallPanic() + } + + ret := mload(0) + } + + /// @dev The function that is temporarily needed to upgrade the SystemContext system contract. This function is to be removed + /// once the upgrade is complete. + /// @dev Checks whether the code hash of the SystemContext contract is correct and updates it if needed. + /// @dev The bootloader calls `setPubdataInfo` before each transaction, including the upgrade one. + /// However, the old SystemContext does not have this method. So the bootloader should invoke this function + /// before starting any transaction. + function upgradeSystemContextIfNeeded() { + let expectedCodeHash := 0x010001b3f2c3a6bdd5ad00ae29a7cbbb32dca3c31fb608b5cd52f8f3056a3847 + + let actualCodeHash := getRawCodeHash(SYSTEM_CONTEXT_ADDR(), true) + if iszero(eq(expectedCodeHash, actualCodeHash)) { + // Now, we need to encode the call to the `ContractDeployer.forceDeployOnAddresses()` function. + + // The `mimicCallOnlyResult` requires that the first word of the data + // contains its length. Here it is 292 bytes. + mstore(0, 292) + mstore(32, 0xe9f18c1700000000000000000000000000000000000000000000000000000000) + + // The 0x20 offset, for the array of forced deployments + mstore(36, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Only one force deployment + mstore(68, 0x0000000000000000000000000000000000000000000000000000000000000001) + + // Now, starts the description of the forced deployment itself. + // Firstly, the offset. + mstore(100, 0x0000000000000000000000000000000000000000000000000000000000000020) + // The new hash of the SystemContext contract. + mstore(132, expectedCodeHash) + // The address of the system context + mstore(164, SYSTEM_CONTEXT_ADDR()) + // The constructor must be called to reset the `blockGasLimit` variable + mstore(196, 0x0000000000000000000000000000000000000000000000000000000000000001) + // The value should be 0. + mstore(228, 0x0000000000000000000000000000000000000000000000000000000000000000) + // The offset of the input array. + mstore(260, 0x00000000000000000000000000000000000000000000000000000000000000a0) + // No input is provided, the array is empty. + mstore(292, 0x0000000000000000000000000000000000000000000000000000000000000000) + + // We'll use a mimicCall to simulate the correct sender. + let success := mimicCallOnlyResult( + CONTRACT_DEPLOYER_ADDR(), + FORCE_DEPLOYER(), + 0, + 0, + 0, + 0, + 0, + 0 + ) + + if iszero(success) { + assertionError("system context upgrade fail") + } + } + } + + /// @dev Calculates the canonical hash of the L1->L2 transaction that will be + /// sent to L1 as a message to the L1 contract that a certain operation has been processed. + function getCanonicalL1TxHash(txDataOffset) -> ret { + // Putting the correct value at the `txDataOffset` just in case, since + // the correctness of this value is not part of the system invariants. + // Note, that the correct ABI encoding of the Transaction structure starts with 0x20 + mstore(txDataOffset, 32) + + let innerTxDataOffset := add(txDataOffset, 32) + let dataLength := safeAdd(32, getDataLength(innerTxDataOffset), "qev") + + debugLog("HASH_OFFSET", innerTxDataOffset) + debugLog("DATA_LENGTH", dataLength) + + ret := keccak256(txDataOffset, dataLength) + } + + /// @dev The purpose of this function is to make sure the operator + /// gets paid for the transaction. Note, that the beneficiary of the payment is + /// bootloader. + /// The operator will be paid at the end of the batch. + function ensurePayment(txDataOffset, gasPrice) { + // Skipping the first 0x20 byte in the encoding of the transaction. + let innerTxDataOffset := add(txDataOffset, 32) + let from := getFrom(innerTxDataOffset) + let requiredETH := safeMul(getGasLimit(innerTxDataOffset), gasPrice, "lal") + + let bootloaderBalanceETH := balance(BOOTLOADER_FORMAL_ADDR()) + let paymaster := getPaymaster(innerTxDataOffset) + + let payer := 0 + + switch paymaster + case 0 { + payer := from + + // There is no paymaster, the user should pay for the execution. + // Calling the `payForTransaction` method of the account. + setHook(VM_HOOK_ACCOUNT_VALIDATION_ENTERED()) + let res := accountPayForTx(from, txDataOffset) + setHook(VM_HOOK_NO_VALIDATION_ENTERED()) + + + if iszero(res) { + revertWithReason( + PAY_FOR_TX_FAILED_ERR_CODE(), + 1 + ) + } + } + default { + // There is some paymaster present. + payer := paymaster + + // Firstly, the `prepareForPaymaster` method of the user's account is called. + setHook(VM_HOOK_ACCOUNT_VALIDATION_ENTERED()) + let userPrePaymasterResult := accountPrePaymaster(from, txDataOffset) + setHook(VM_HOOK_NO_VALIDATION_ENTERED()) + + if iszero(userPrePaymasterResult) { + revertWithReason( + PRE_PAYMASTER_PREPARATION_FAILED_ERR_CODE(), + 1 + ) + } + + // Then, the paymaster is called. The paymaster should pay us in this method. + setHook(VM_HOOK_PAYMASTER_VALIDATION_ENTERED()) + let paymasterPaymentSuccess := validateAndPayForPaymasterTransaction(paymaster, txDataOffset) + if iszero(paymasterPaymentSuccess) { + revertWithReason( + PAYMASTER_VALIDATION_FAILED_ERR_CODE(), + 1 + ) + } + + storePaymasterContextAndCheckMagic() + setHook(VM_HOOK_NO_VALIDATION_ENTERED()) + } + + let bootloaderReceivedFunds := safeSub(balance(BOOTLOADER_FORMAL_ADDR()), bootloaderBalanceETH, "qsx") + + // If the amount of funds provided to the bootloader is less than the minimum required one + // then this transaction should be rejected. + if lt(bootloaderReceivedFunds, requiredETH) { + revertWithReason( + FAILED_TO_CHARGE_FEE_ERR_CODE(), + 0 + ) + } + + let excessiveFunds := safeSub(bootloaderReceivedFunds, requiredETH, "llm") + + if gt(excessiveFunds, 0) { + // Returning back the excessive funds taken. + directETHTransfer(excessiveFunds, payer) + } + } + + /// @notice Mints ether to the recipient + /// @param to -- the address of the recipient + /// @param amount -- the amount of ETH to mint + /// @param useNearCallPanic -- whether to use nearCallPanic in case of + /// the transaction failing to execute. It is desirable in cases + /// where we want to allow the method fail without reverting the entire bootloader + function mintEther(to, amount, useNearCallPanic) { + mstore(0, 0x40c10f1900000000000000000000000000000000000000000000000000000000) + mstore(4, to) + mstore(36, amount) + let success := call( + gas(), + ETH_L2_TOKEN_ADDR(), + 0, + 0, + 68, + 0, + 0 + ) + if iszero(success) { + switch useNearCallPanic + case 0 { + revertWithReason( + MINT_ETHER_FAILED_ERR_CODE(), + 0 + ) + } + default { + nearCallPanic() + } + } + } + + /// @dev Saves the paymaster context and checks that the paymaster has returned the correct + /// magic value. + /// @dev IMPORTANT: this method should be called right after + /// the validateAndPayForPaymasterTransaction method to keep the `returndata` from that transaction + function storePaymasterContextAndCheckMagic() { + // The paymaster validation step should return context of type "bytes context" + // This means that the returndata is encoded the following way: + // 0x20 || context_len || context_bytes... + let returnlen := returndatasize() + // The minimal allowed returndatasize is 64: magicValue || offset + if lt(returnlen, 64) { + revertWithReason( + PAYMASTER_RETURNED_INVALID_CONTEXT(), + 0 + ) + } + + // Note that it is important to copy the magic even though it is not needed if the + // `SHOULD_ENSURE_CORRECT_RETURNED_MAGIC` is false. It is never false in production + // but it is so in fee estimation and we want to preserve as many operations as + // in the original operation. + { + returndatacopy(0, 0, 32) + let magic := mload(0) + + let isMagicCorrect := eq(magic, 0x038a24bc00000000000000000000000000000000000000000000000000000000) + + if and(iszero(isMagicCorrect), SHOULD_ENSURE_CORRECT_RETURNED_MAGIC()) { + revertWithReason( + PAYMASTER_RETURNED_INVALID_MAGIC_ERR_CODE(), + 0 + ) + } + } + + returndatacopy(0, 32, 32) + let returnedContextOffset := mload(0) + + // Ensuring that the returned offset is not greater than the returndata length + // Note, that we cannot use addition here to prevent an overflow + if gt(returnedContextOffset, returnlen) { + revertWithReason( + PAYMASTER_RETURNED_INVALID_CONTEXT(), + 0 + ) + } + + // Can not read the returned length. + // It is safe to add here due to the previous check. + if gt(add(returnedContextOffset, 32), returnlen) { + revertWithReason( + PAYMASTER_RETURNED_INVALID_CONTEXT(), + 0 + ) + } + + // Reading the length of the context + returndatacopy(0, returnedContextOffset, 32) + let returnedContextLen := mload(0) + + // Ensuring that returnedContextLen is not greater than the length of the paymaster context + // Note, that this check at the same time prevents an overflow in the future operations with returnedContextLen + if gt(returnedContextLen, PAYMASTER_CONTEXT_BYTES()) { + revertWithReason( + PAYMASTER_RETURNED_CONTEXT_IS_TOO_LONG(), + 0 + ) + } + + let roundedContextLen := lengthRoundedByWords(returnedContextLen) + + // The returned context's size should not exceed the maximum length + if gt(add(roundedContextLen, 32), PAYMASTER_CONTEXT_BYTES()) { + revertWithReason( + PAYMASTER_RETURNED_CONTEXT_IS_TOO_LONG(), + 0 + ) + } + + if gt(add(returnedContextOffset, add(32, returnedContextLen)), returnlen) { + revertWithReason( + PAYMASTER_RETURNED_INVALID_CONTEXT(), + 0 + ) + } + + returndatacopy(PAYMASTER_CONTEXT_BEGIN_BYTE(), returnedContextOffset, add(32, returnedContextLen)) + } + + /// @dev The function responsible for processing L1->L2 transactions. + /// @param txDataOffset The offset to the transaction's information + /// @param resultPtr The pointer at which the result of the execution of this transaction + /// @param transactionIndex The index of the transaction + /// @param gasPerPubdata The price per pubdata to be used + /// @param isPriorityOp Whether the transaction is a priority one + function processL1Tx( + txDataOffset, + resultPtr, + transactionIndex, + gasPerPubdata, + isPriorityOp + ) { + // For L1->L2 transactions we always use the pubdata price provided by the transaction. + // This is needed to ensure DDoS protection. All the excess expenditure + // will be refunded to the user. + + // Skipping the first formal 0x20 byte + let innerTxDataOffset := add(txDataOffset, 32) + + let basePubdataSpent := getPubdataCounter() + + let gasLimitForTx, reservedGas := getGasLimitForTx( + innerTxDataOffset, + transactionIndex, + gasPerPubdata, + L1_TX_INTRINSIC_L2_GAS(), + L1_TX_INTRINSIC_PUBDATA() + ) + + let gasUsedOnPreparation := 0 + let canonicalL1TxHash := 0 + + canonicalL1TxHash, gasUsedOnPreparation := l1TxPreparation(txDataOffset, gasPerPubdata, basePubdataSpent) + + let refundGas := 0 + let success := 0 + + // The invariant that the user deposited more than the value needed + // for the transaction must be enforced on L1, but we double check it here + let gasLimit := getGasLimit(innerTxDataOffset) + + // Note, that for now the property of block.base <= tx.maxFeePerGas does not work + // for L1->L2 transactions. For now, these transactions are processed with the same gasPrice + // they were provided on L1. In the future, we may apply a new logic for it. + let gasPrice := getMaxFeePerGas(innerTxDataOffset) + let txInternalCost := safeMul(gasPrice, gasLimit, "poa") + let value := getValue(innerTxDataOffset) + if lt(getReserved0(innerTxDataOffset), safeAdd(value, txInternalCost, "ol")) { + assertionError("deposited eth too low") + } + + // In previous steps, there might have been already some pubdata published (e.g. to mark factory dependencies as published). + // However, these actions are mandatory and it is assumed that the L1 Mailbox contract ensured that the provided gas is enough to cover for pubdata. + if gt(gasLimitForTx, gasUsedOnPreparation) { + let gasSpentOnExecution := 0 + let gasForExecution := sub(gasLimitForTx, gasUsedOnPreparation) + + gasSpentOnExecution, success := getExecuteL1TxAndNotifyResult( + txDataOffset, + gasForExecution, + basePubdataSpent, + gasPerPubdata, + ) + + let ergsSpentOnPubdata := getErgsSpentForPubdata( + basePubdataSpent, + gasPerPubdata + ) + + // It is assumed that `isNotEnoughGasForPubdata` ensured that the user did not publish too much pubdata. + let potentialRefund := saturatingSub( + safeAdd(reservedGas, gasForExecution, "safeadd: potentialRefund1"), + safeAdd(gasSpentOnExecution, ergsSpentOnPubdata, "safeadd: potentialRefund2") + ) + + // Asking the operator for refund + askOperatorForRefund(potentialRefund, ergsSpentOnPubdata, gasPerPubdata) + + // In case the operator provided smaller refund than the one calculated + // by the bootloader, we return the refund calculated by the bootloader. + refundGas := max(getOperatorRefundForTx(transactionIndex), potentialRefund) + } + + if gt(refundGas, gasLimit) { + assertionError("L1: refundGas > gasLimit") + } + + let payToOperator := safeMul(gasPrice, safeSub(gasLimit, refundGas, "lpah"), "mnk") + + notifyAboutRefund(refundGas) + + // Paying the fee to the operator + mintEther(BOOTLOADER_FORMAL_ADDR(), payToOperator, false) + + let toRefundRecipient + switch success + case 0 { + if iszero(isPriorityOp) { + // Upgrade transactions must always succeed + assertionError("Upgrade tx failed") + } + + // If the transaction reverts, then minting the msg.value to the user has been reverted + // as well, so we can simply mint everything that the user has deposited to + // the refund recipient + toRefundRecipient := safeSub(getReserved0(innerTxDataOffset), payToOperator, "vji") + } + default { + // If the transaction succeeds, then it is assumed that msg.value was transferred correctly. However, the remaining + // ETH deposited will be given to the refund recipient. + + toRefundRecipient := safeSub(getReserved0(innerTxDataOffset), safeAdd(getValue(innerTxDataOffset), payToOperator, "kpa"), "ysl") + } + + if gt(toRefundRecipient, 0) { + let refundRecipient := getReserved1(innerTxDataOffset) + // Zero out the first 12 bytes to be sure that refundRecipient is address. + // In case of an issue in L1 contracts, we still will be able to process tx. + refundRecipient := and(refundRecipient, sub(shl(160, 1), 1)) + mintEther(refundRecipient, toRefundRecipient, false) + } + + mstore(resultPtr, success) + + debugLog("Send message to L1", success) + + // Sending the L2->L1 log so users will be able to prove transaction execution result on L1. + sendL2LogUsingL1Messenger(true, canonicalL1TxHash, success) + + if isPriorityOp { + // Update priority txs L1 data + mstore(0, mload(PRIORITY_TXS_L1_DATA_BEGIN_BYTE())) + mstore(32, canonicalL1TxHash) + mstore(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), keccak256(0, 64)) + mstore(add(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), 32), add(mload(add(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), 32)), 1)) + } + } + + /// @dev The function responsible for execution of L1->L2 transactions. + /// @param txDataOffset The offset to the transaction's information + /// @param gasForExecution The amount of gas available for the execution + /// @param basePubdataSpent The amount of pubdata spent at the start of the transaction + /// @param gasPerPubdata The price per each pubdata byte in L2 gas + function getExecuteL1TxAndNotifyResult( + txDataOffset, + gasForExecution, + basePubdataSpent, + gasPerPubdata + ) -> gasSpentOnExecution, success { + debugLog("gasForExecution", gasForExecution) + + let callAbi := getNearCallABI(gasForExecution) + debugLog("callAbi", callAbi) + + checkEnoughGas(gasForExecution) + + let gasBeforeExecution := gas() + success := ZKSYNC_NEAR_CALL_executeL1Tx( + callAbi, + txDataOffset, + basePubdataSpent, + gasPerPubdata + ) + notifyExecutionResult(success) + gasSpentOnExecution := sub(gasBeforeExecution, gas()) + } + + /// @dev The function responsible for doing all the pre-execution operations for L1->L2 transactions. + /// @param txDataOffset The offset to the transaction's information + /// @param gasPerPubdata The price per each pubdata byte in L2 gas + /// @param basePubdataSpent The amount of pubdata spent at the start of the transaction + /// @return canonicalL1TxHash The hash of processed L1->L2 transaction + /// @return gasUsedOnPreparation The number of L2 gas used in the preparation stage + function l1TxPreparation( + txDataOffset, + gasPerPubdata, + basePubdataSpent + ) -> canonicalL1TxHash, gasUsedOnPreparation { + let innerTxDataOffset := add(txDataOffset, 32) + + setPubdataInfo(gasPerPubdata, basePubdataSpent) + + let gasBeforePreparation := gas() + debugLog("gasBeforePreparation", gasBeforePreparation) + + // Even though the smart contracts on L1 should make sure that the L1->L2 provide enough gas to generate the hash + // we should still be able to do it even if this protection layer fails. + canonicalL1TxHash := getCanonicalL1TxHash(txDataOffset) + debugLog("l1 hash", canonicalL1TxHash) + + // Appending the transaction's hash to the current L2 block + appendTransactionHash(canonicalL1TxHash, true) + + markFactoryDepsForTx(innerTxDataOffset, true) + + gasUsedOnPreparation := safeSub(gasBeforePreparation, gas(), "xpa") + debugLog("gasUsedOnPreparation", gasUsedOnPreparation) + } + + /// @dev Returns the gas price that should be used by the transaction + /// based on the EIP1559's maxFeePerGas and maxPriorityFeePerGas. + /// The following invariants should hold: + /// maxPriorityFeePerGas <= maxFeePerGas + /// baseFee <= maxFeePerGas + /// While we charge baseFee from the users, the method is mostly used as a method for validating + /// the correctness of the fee parameters + function getGasPrice( + maxFeePerGas, + maxPriorityFeePerGas + ) -> ret { + let baseFee := basefee() + + if gt(maxPriorityFeePerGas, maxFeePerGas) { + revertWithReason( + MAX_PRIORITY_FEE_PER_GAS_GREATER_THAN_MAX_FEE_PER_GAS(), + 0 + ) + } + + if gt(baseFee, maxFeePerGas) { + revertWithReason( + BASE_FEE_GREATER_THAN_MAX_FEE_PER_GAS(), + 0 + ) + } + + // We always use `baseFee` to charge the transaction + ret := baseFee + } + + /// @dev The function responsible for processing L2 transactions. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param resultPtr The pointer at which the result of the execution of this transaction + /// should be stored. + /// @param transactionIndex The index of the current transaction. + /// @param gasPerPubdata The L2 gas to be used for each byte of pubdata published onchain. + /// @dev This function firstly does the validation step and then the execution step in separate near_calls. + /// It is important that these steps are split to avoid rollbacking the state made by the validation step. + function processL2Tx( + txDataOffset, + resultPtr, + transactionIndex, + gasPerPubdata + ) { + let basePubdataSpent := getPubdataCounter() + + debugLog("baseSpent", basePubdataSpent) + + let innerTxDataOffset := add(txDataOffset, 32) + + // Firstly, we publish all the bytecodes needed. This is needed to be done separately, since + // bytecodes usually form the bulk of the L2 gas prices. + + let gasLimitForTx, reservedGas := getGasLimitForTx( + innerTxDataOffset, + transactionIndex, + gasPerPubdata, + L2_TX_INTRINSIC_GAS(), + L2_TX_INTRINSIC_PUBDATA() + ) + + let gasPrice := getGasPrice(getMaxFeePerGas(innerTxDataOffset), getMaxPriorityFeePerGas(innerTxDataOffset)) + + debugLog("gasLimitForTx", gasLimitForTx) + + let gasLeft := l2TxValidation( + txDataOffset, + gasLimitForTx, + gasPrice, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) + + debugLog("validation finished", 0) + + let gasSpentOnExecute := 0 + let success := 0 + success, gasSpentOnExecute := l2TxExecution(txDataOffset, gasLeft, basePubdataSpent, reservedGas, gasPerPubdata) + + debugLog("execution finished", 0) + + let refund := 0 + let gasToRefund := saturatingSub(gasLeft, gasSpentOnExecute) + + // Note, that we pass reservedGas from the refundGas separately as it should not be used + // during the postOp execution. + refund := refundCurrentL2Transaction( + txDataOffset, + transactionIndex, + success, + gasToRefund, + gasPrice, + reservedGas, + basePubdataSpent, + gasPerPubdata + ) + + notifyAboutRefund(refund) + mstore(resultPtr, success) + } + + /// @dev Calculates the L2 gas limit for the transaction + /// @param innerTxDataOffset The offset for the ABI-encoded Transaction struct fields. + /// @param transactionIndex The index of the transaction within the batch. + /// @param gasPerPubdata The price for a pubdata byte in L2 gas. + /// @param intrinsicGas The intrinsic number of L2 gas required for transaction processing. + /// @param intrinsicPubdata The intrinsic number of pubdata bytes required for transaction processing. + /// @return gasLimitForTx The maximum number of L2 gas that can be spent on a transaction. + /// @return reservedGas The number of L2 gas that is beyond the `MAX_GAS_PER_TRANSACTION` and beyond the operator's trust limit, + /// i.e. gas which we cannot allow the transaction to use and will refund. + function getGasLimitForTx( + innerTxDataOffset, + transactionIndex, + gasPerPubdata, + intrinsicGas, + intrinsicPubdata + ) -> gasLimitForTx, reservedGas { + let totalGasLimit := getGasLimit(innerTxDataOffset) + + // `MAX_GAS_PER_TRANSACTION` is the amount of gas each transaction + // is guaranteed to get, so even if the operator does not trust the account enough, + // it is still obligated to provide at least that + let operatorTrustedGasLimit := max(MAX_GAS_PER_TRANSACTION(), getOperatorTrustedGasLimitForTx(transactionIndex)) + + // We remember the amount of gas that is beyond the operator's trust limit to refund it back later. + switch gt(totalGasLimit, operatorTrustedGasLimit) + case 0 { + reservedGas := 0 + } + default { + reservedGas := sub(totalGasLimit, operatorTrustedGasLimit) + totalGasLimit := operatorTrustedGasLimit + } + + let txEncodingLen := safeAdd(32, getDataLength(innerTxDataOffset), "lsh") + + let operatorOverheadForTransaction := getVerifiedOperatorOverheadForTx( + transactionIndex, + totalGasLimit, + txEncodingLen + ) + gasLimitForTx := safeSub(totalGasLimit, operatorOverheadForTransaction, "qr") + + let intrinsicOverhead := safeAdd( + intrinsicGas, + // the error messages are trimmed to fit into 32 bytes + safeMul(intrinsicPubdata, gasPerPubdata, "qw"), + "fj" + ) + + switch lt(gasLimitForTx, intrinsicOverhead) + case 1 { + gasLimitForTx := 0 + } + default { + gasLimitForTx := sub(gasLimitForTx, intrinsicOverhead) + } + } + + /// @dev The function responsible for the L2 transaction validation. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param gasLimitForTx The L2 gas limit for the transaction validation & execution. + /// @param gasPrice The L2 gas price that should be used by the transaction. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + /// @return gasLeft The gas left after the validation step. + function l2TxValidation( + txDataOffset, + gasLimitForTx, + gasPrice, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) -> gasLeft { + let gasBeforeValidate := gas() + + debugLog("gasBeforeValidate", gasBeforeValidate) + + // Saving the tx hash and the suggested signed tx hash to memory + saveTxHashes(txDataOffset) + + // Appending the transaction's hash to the current L2 block + appendTransactionHash(mload(CURRENT_L2_TX_HASHES_BEGIN_BYTE()), false) + + setPubdataInfo(gasPerPubdata, basePubdataSpent) + + checkEnoughGas(gasLimitForTx) + + // Note, that it is assumed that `ZKSYNC_NEAR_CALL_validateTx` will always return true + // unless some error which made the whole bootloader to revert has happened or + // it runs out of gas. + let isValid := 0 + + // Only if the gasLimit for tx is non-zero, we will try to actually run the validation + if gasLimitForTx { + let validateABI := getNearCallABI(gasLimitForTx) + + debugLog("validateABI", validateABI) + + isValid := ZKSYNC_NEAR_CALL_validateTx(validateABI, txDataOffset, gasPrice) + } + + debugLog("isValid", isValid) + + let gasUsedForValidate := sub(gasBeforeValidate, gas()) + debugLog("gasUsedForValidate", gasUsedForValidate) + + gasLeft := saturatingSub(gasLimitForTx, gasUsedForValidate) + + // isValid can only be zero if the validation has failed with out of gas + if or(iszero(gasLeft), iszero(isValid)) { + revertWithReason(TX_VALIDATION_OUT_OF_GAS(), 0) + } + + if isNotEnoughGasForPubdata( + basePubdataSpent, gasLeft, reservedGas, gasPerPubdata + ) { + revertWithReason(TX_VALIDATION_OUT_OF_GAS(), 0) + } + + setHook(VM_HOOK_VALIDATION_STEP_ENDED()) + } + + /// @dev The function responsible for the execution step of the L2 transaction. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param gasLeft The gas left after the validation step. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + /// @return success Whether or not the execution step was successful. + /// @return gasSpentOnExecute The gas spent on the transaction execution. + function l2TxExecution( + txDataOffset, + gasLeft, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) -> success, gasSpentOnExecute { + let newCompressedFactoryDepsPointer := 0 + let gasSpentOnFactoryDeps := 0 + let gasBeforeFactoryDeps := gas() + if gasLeft { + let markingDependenciesABI := getNearCallABI(gasLeft) + checkEnoughGas(gasLeft) + newCompressedFactoryDepsPointer := ZKSYNC_NEAR_CALL_markFactoryDepsL2( + markingDependenciesABI, + txDataOffset, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) + gasSpentOnFactoryDeps := sub(gasBeforeFactoryDeps, gas()) + } + + // If marking of factory dependencies has been unsuccessful, 0 value is returned. + // Otherwise, all the previous dependencies have been successfully published, so + // we need to move the pointer. + if newCompressedFactoryDepsPointer { + mstore(COMPRESSED_BYTECODES_BEGIN_BYTE(), newCompressedFactoryDepsPointer) + } + + switch gt(gasLeft, gasSpentOnFactoryDeps) + case 0 { + gasSpentOnExecute := gasLeft + gasLeft := 0 + } + default { + // Note, that since gt(gasLeft, gasSpentOnFactoryDeps) = true + // sub(gasLeft, gasSpentOnFactoryDeps) > 0, which is important + // because a nearCall with 0 gas passes on all the gas of the parent frame. + gasLeft := sub(gasLeft, gasSpentOnFactoryDeps) + + let executeABI := getNearCallABI(gasLeft) + checkEnoughGas(gasLeft) + + let gasBeforeExecute := gas() + // for this one, we don't care whether or not it fails. + success := ZKSYNC_NEAR_CALL_executeL2Tx( + executeABI, + txDataOffset, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) + + gasSpentOnExecute := add(gasSpentOnFactoryDeps, sub(gasBeforeExecute, gas())) + } + + debugLog("notifySuccess", success) + + notifyExecutionResult(success) + } + + /// @dev Function responsible for the validation & fee payment step of the transaction. + /// @param abi The nearCall ABI. It is implicitly used as gasLimit for the call of this function. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param gasPrice The gasPrice to be used in this transaction. + function ZKSYNC_NEAR_CALL_validateTx( + abi, + txDataOffset, + gasPrice + ) -> ret { + // For the validation step we always use the bootloader as the tx.origin of the transaction + setTxOrigin(BOOTLOADER_FORMAL_ADDR()) + setGasPrice(gasPrice) + + // Skipping the first 0x20 word of the ABI-encoding + let innerTxDataOffset := add(txDataOffset, 32) + debugLog("Starting validation", 0) + + accountValidateTx(txDataOffset) + debugLog("Tx validation complete", 1) + + ensurePayment(txDataOffset, gasPrice) + + ret := 1 + } + + /// @dev Function responsible for the execution of the L2 transaction. + /// It includes both the call to the `executeTransaction` method of the account + /// and the call to postOp of the account. + /// @param abi The nearCall ABI. It is implicitly used as gasLimit for the call of this function. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + function ZKSYNC_NEAR_CALL_executeL2Tx( + abi, + txDataOffset, + basePubdataSpent, + reservedGas, + gasPerPubdata, + ) -> success { + // Skipping the first word of the ABI-encoding encoding + let innerTxDataOffset := add(txDataOffset, 32) + let from := getFrom(innerTxDataOffset) + + debugLog("Executing L2 tx", 0) + // The tx.origin can only be an EOA + switch isEOA(from) + case true { + setTxOrigin(from) + } + default { + setTxOrigin(BOOTLOADER_FORMAL_ADDR()) + } + + success := executeL2Tx(txDataOffset, from) + + if isNotEnoughGasForPubdata( + basePubdataSpent, + gas(), + reservedGas, + gasPerPubdata + ) { + // If not enough gas for pubdata was provided, we revert all the state diffs / messages + // that caused the pubdata to be published + nearCallPanic() + } + + debugLog("Executing L2 ret", success) + } + + /// @dev Sets factory dependencies for an L2 transaction with possible usage of packed bytecodes. + /// @param abi The nearCall ABI. It is implicitly used as gasLimit for the call of this function. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + function ZKSYNC_NEAR_CALL_markFactoryDepsL2( + abi, + txDataOffset, + basePubdataSpent, + reservedGas, + gasPerPubdata + ) -> newDataInfoPtr { + let innerTxDataOffset := add(txDataOffset, 32) + + /// Note, that since it is the near call when it panics it reverts the state changes, but it DOES NOT + /// revert the changes in *memory* of the current frame. That is why we do not change the value under + /// COMPRESSED_BYTECODES_BEGIN_BYTE(), and it is only changed outside of this method. + let dataInfoPtr := mload(COMPRESSED_BYTECODES_BEGIN_BYTE()) + let factoryDepsPtr := getFactoryDepsPtr(innerTxDataOffset) + let factoryDepsLength := mload(factoryDepsPtr) + + let iter := add(factoryDepsPtr, 32) + let endPtr := add(iter, mul(32, factoryDepsLength)) + + for { } lt(iter, endPtr) { iter := add(iter, 32)} { + let bytecodeHash := mload(iter) + + let currentExpectedBytecodeHash := mload(dataInfoPtr) + + if eq(bytecodeHash, currentExpectedBytecodeHash) { + // Here we are making sure that the bytecode is indeed not yet know and needs to be published, + // preventing users from being overcharged by the operator. + let marker := getCodeMarker(bytecodeHash) + + if marker { + assertionError("invalid republish") + } + + dataInfoPtr := sendCompressedBytecode(dataInfoPtr, bytecodeHash) + } + } + + // For all the bytecodes that have not been compressed on purpose or due to the inefficiency + // of compressing the entire preimage of the bytecode will be published. + // For bytecodes published in the previous step, no need pubdata will have to be published + markFactoryDepsForTx(innerTxDataOffset, false) + + if isNotEnoughGasForPubdata( + basePubdataSpent, + gas(), + reservedGas, + gasPerPubdata + ) { + // If not enough gas for pubdata was provided, we revert all the state diffs / messages + // that caused the pubdata to be published + nearCallPanic() + } + + newDataInfoPtr := dataInfoPtr + } + + function getCodeMarker(bytecodeHash) -> ret { + mstore(0, 0x4c6314f000000000000000000000000000000000000000000000000000000000) + mstore(4, bytecodeHash) + let success := call( + gas(), + KNOWN_CODES_CONTRACT_ADDR(), + 0, + 0, + 36, + 0, + 32 + ) + + if iszero(success) { + nearCallPanic() + } + + ret := mload(0) + } + + + /// @dev Used to refund the current transaction. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param transactionIndex The index of the transaction in the batch. + /// @param success The transaction execution status. + /// @param gasLeft The gas left after the execution step. + /// @param gasPrice The L2 gas price that should be used by the transaction. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + /// The gas that this transaction consumes has been already paid in the + /// process of the validation + function refundCurrentL2Transaction( + txDataOffset, + transactionIndex, + success, + gasLeft, + gasPrice, + reservedGas, + basePubdataSpent, + gasPerPubdata + ) -> finalRefund { + setTxOrigin(BOOTLOADER_FORMAL_ADDR()) + + finalRefund := 0 + + let innerTxDataOffset := add(txDataOffset, 32) + + let paymaster := getPaymaster(innerTxDataOffset) + let refundRecipient := 0 + switch paymaster + case 0 { + // No paymaster means that the sender should receive the refund + refundRecipient := getFrom(innerTxDataOffset) + } + default { + refundRecipient := paymaster + + if gt(gasLeft, 0) { + checkEnoughGas(gasLeft) + let nearCallAbi := getNearCallABI(gasLeft) + let gasBeforePostOp := gas() + + let spentOnPubdata := getErgsSpentForPubdata( + basePubdataSpent, + gasPerPubdata + ) + + pop(ZKSYNC_NEAR_CALL_callPostOp( + // Maximum number of gas that the postOp could spend + nearCallAbi, + paymaster, + txDataOffset, + success, + // Since the paymaster will be refunded with reservedGas, + // it should know about it + saturatingSub(safeAdd(gasLeft, reservedGas, "jkl"), spentOnPubdata), + basePubdataSpent, + gasPerPubdata, + reservedGas + )) + let gasSpentByPostOp := sub(gasBeforePostOp, gas()) + + gasLeft := saturatingSub(gasLeft, gasSpentByPostOp) + } + } + + // It was expected that before this point various `isNotEnoughGasForPubdata` methods would ensure that the user + // has enough funds for pubdata. Now, we just subtract the leftovers from the user. + let spentOnPubdata := getErgsSpentForPubdata( + basePubdataSpent, + gasPerPubdata + ) + + let totalRefund := saturatingSub(add(reservedGas, gasLeft), spentOnPubdata) + + askOperatorForRefund( + totalRefund, + spentOnPubdata, + gasPerPubdata + ) + + let operatorProvidedRefund := getOperatorRefundForTx(transactionIndex) + + // If the operator provides the value that is lower than the one suggested for + // the bootloader, we will use the one calculated by the bootloader. + let refundInGas := max(operatorProvidedRefund, totalRefund) + + // The operator cannot refund more than the gasLimit for the transaction + if gt(refundInGas, getGasLimit(innerTxDataOffset)) { + assertionError("refundInGas > gasLimit") + } + + if iszero(validateUint64(refundInGas)) { + assertionError("refundInGas is not uint64") + } + + let ethToRefund := safeMul( + refundInGas, + gasPrice, + "fdf" + ) + + directETHTransfer(ethToRefund, refundRecipient) + + finalRefund := refundInGas + } + + /// @notice A function that transfers ETH directly through the L2BaseToken system contract. + /// Note, that unlike classical EVM transfers it does NOT call the recipient, but only changes the balance. + function directETHTransfer(amount, recipient) { + let ptr := 0 + mstore(ptr, 0x579952fc00000000000000000000000000000000000000000000000000000000) + mstore(add(ptr, 4), BOOTLOADER_FORMAL_ADDR()) + mstore(add(ptr, 36), recipient) + mstore(add(ptr, 68), amount) + + let transferSuccess := call( + gas(), + ETH_L2_TOKEN_ADDR(), + 0, + 0, + 100, + 0, + 0 + ) + + if iszero(transferSuccess) { + assertionError("Failed to refund") + } + } + + /// @dev Return the operator suggested transaction refund. + function getOperatorRefundForTx(transactionIndex) -> ret { + let refundPtr := add(TX_OPERATOR_REFUND_BEGIN_BYTE(), mul(transactionIndex, 32)) + ret := mload(refundPtr) + } + + /// @dev Return the operator suggested transaction overhead cost. + function getOperatorOverheadForTx(transactionIndex) -> ret { + let txBatchOverheadPtr := add(TX_SUGGESTED_OVERHEAD_BEGIN_BYTE(), mul(transactionIndex, 32)) + ret := mload(txBatchOverheadPtr) + } + + /// @dev Return the operator's "trusted" transaction gas limit + function getOperatorTrustedGasLimitForTx(transactionIndex) -> ret { + let txTrustedGasLimitPtr := add(TX_OPERATOR_TRUSTED_GAS_LIMIT_BEGIN_BYTE(), mul(transactionIndex, 32)) + ret := mload(txTrustedGasLimitPtr) + } + + /// @dev Returns the bytecode hash that is next for being published + function getCurrentCompressedBytecodeHash() -> ret { + let compressionPtr := mload(COMPRESSED_BYTECODES_BEGIN_BYTE()) + + ret := mload(add(COMPRESSED_BYTECODES_BEGIN_BYTE(), compressionPtr)) + } + + function checkOffset(pointer) { + if gt(pointer, sub(COMPRESSED_BYTECODES_END_BYTE(), MIN_ALLOWED_OFFSET_FOR_COMPRESSED_BYTES_POINTER())) { + assertionError("calldataEncoding too big") + } + } + + /// @dev It is expected that the pointer at the COMPRESSED_BYTECODES_BEGIN_BYTE() + /// stores the position of the current bytecodeHash + function sendCompressedBytecode(dataInfoPtr, bytecodeHash) -> ret { + // Storing the right selector, ensuring that the operator cannot manipulate it + mstore(safeAdd(dataInfoPtr, 32, "vmt"), 0xf5e69a47) + + let calldataPtr := safeAdd(dataInfoPtr, 60, "vty") + let afterSelectorPtr := safeAdd(calldataPtr, 4, "vtu") + + let originalBytecodeOffset := safeAdd(mload(afterSelectorPtr), afterSelectorPtr, "vtr") + checkOffset(originalBytecodeOffset) + let potentialRawCompressedDataOffset := validateBytes( + originalBytecodeOffset + ) + + if iszero(eq(originalBytecodeOffset, safeAdd(afterSelectorPtr, 64, "vtp"))) { + assertionError("Compression calldata incorrect") + } + + let rawCompressedDataOffset := safeAdd(mload(safeAdd(afterSelectorPtr, 32, "ewq")), afterSelectorPtr, "vbt") + checkOffset(rawCompressedDataOffset) + + if iszero(eq(potentialRawCompressedDataOffset, rawCompressedDataOffset)) { + assertionError("Compression calldata incorrect") + } + + let nextAfterCalldata := validateBytes( + rawCompressedDataOffset + ) + checkOffset(nextAfterCalldata) + + let totalLen := safeSub(nextAfterCalldata, calldataPtr, "xqwf") + let success := call( + gas(), + BYTECODE_COMPRESSOR_ADDR(), + 0, + calldataPtr, + totalLen, + 0, + 32 + ) + + // If the transaction failed, either there was not enough gas or compression is malformed. + if iszero(success) { + debugLog("compressor call failed", 0) + debugReturndata() + nearCallPanic() + } + + let returnedBytecodeHash := mload(0) + + // If the bytecode hash calculated on the bytecode compressor's side + // is not equal to the one provided by the operator means that the operator is + // malicious and we should revert the batch altogether + if iszero(eq(returnedBytecodeHash, bytecodeHash)) { + assertionError("bytecodeHash incorrect") + } + + ret := nextAfterCalldata + } + + /// @dev Get checked for overcharged operator's overhead for the transaction. + /// @param transactionIndex The index of the transaction in the batch + /// @param txTotalGasLimit The total gass limit of the transaction (including the overhead). + /// @param gasPerPubdataByte The price for pubdata byte in gas. + /// @param txEncodeLen The length of the ABI-encoding of the transaction + function getVerifiedOperatorOverheadForTx( + transactionIndex, + txTotalGasLimit, + txEncodeLen + ) -> ret { + let operatorOverheadForTransaction := getOperatorOverheadForTx(transactionIndex) + if gt(operatorOverheadForTransaction, txTotalGasLimit) { + assertionError("Overhead higher than gasLimit") + } + + let requiredOverhead := getTransactionUpfrontOverhead(txEncodeLen) + + debugLog("txTotalGasLimit", txTotalGasLimit) + debugLog("requiredOverhead", requiredOverhead) + debugLog("operatorOverheadForTransaction", operatorOverheadForTransaction) + + // The required overhead is less than the overhead that the operator + // has requested from the user, meaning that the operator tried to overcharge the user + if lt(requiredOverhead, operatorOverheadForTransaction) { + assertionError("Operator's overhead too high") + } + + ret := operatorOverheadForTransaction + } + + /// @dev Function responsible for the execution of the L1->L2 transaction. + /// @param abi The nearCall ABI. It is implicitly used as gasLimit for the call of this function. + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + function ZKSYNC_NEAR_CALL_executeL1Tx( + abi, + txDataOffset, + basePubdataSpent, + gasPerPubdata, + ) -> success { + // Skipping the first word of the ABI encoding of the struct + let innerTxDataOffset := add(txDataOffset, 32) + let from := getFrom(innerTxDataOffset) + let gasPrice := getMaxFeePerGas(innerTxDataOffset) + + debugLog("Executing L1 tx", 0) + debugLog("from", from) + debugLog("gasPrice", gasPrice) + + // We assume that addresses of smart contracts on zkSync and Ethereum + // never overlap, so no need to check whether `from` is an EOA here. + debugLog("setting tx origin", from) + + setTxOrigin(from) + debugLog("setting gas price", gasPrice) + + setGasPrice(gasPrice) + + debugLog("execution itself", 0) + + let value := getValue(innerTxDataOffset) + if value { + mintEther(from, value, true) + } + + success := executeL1Tx(innerTxDataOffset, from) + + debugLog("Executing L1 ret", success) + + // If the success is zero, we will revert in order + // to revert the minting of ether to the user + if iszero(success) { + nearCallPanic() + } + + if isNotEnoughGasForPubdata( + basePubdataSpent, + gas(), + // Note, that for L1->L2 transactions the reserved gas is used to protect the operator from + // transactions that might accidentally cause to publish too many pubdata. + // Thus, even if there is some accidental `reservedGas` left, it should not be used to publish pubdata. + 0, + gasPerPubdata, + ) { + // If not enough gas for pubdata was provided, we revert all the state diffs / messages + // that caused the pubdata to be published + nearCallPanic() + } + } + + /// @dev Returns the ABI for nearCalls. + /// @param gasLimit The gasLimit for this nearCall + function getNearCallABI(gasLimit) -> ret { + ret := gasLimit + } + + /// @dev Used to panic from the nearCall without reverting the parent frame. + /// If you use `revert(...)`, the error will bubble up from the near call and + /// make the bootloader to revert as well. This method allows to exit the nearCall only. + function nearCallPanic() { + // Here we exhaust all the gas of the current frame. + // This will cause the execution to panic. + // Note, that it will cause only the inner call to panic. + precompileCall(gas()) + } + + /// @dev Executes the `precompileCall` opcode. + /// Since the bootloader has no implicit meaning for this opcode, + /// this method just burns gas. + function precompileCall(gasToBurn) { + // We don't care about the return value, since it is a opcode simulation + // and the return value doesn't have any meaning. + let ret := verbatim_2i_1o("precompile", 0, gasToBurn) + } + + /// @dev Returns the pointer to the latest returndata. + function returnDataPtr() -> ret { + ret := verbatim_0i_1o("get_global::ptr_return_data") + } + + + + /// @dev Given the callee and the data to be called with, + /// this function returns whether the mimicCall should use the `isSystem` flag. + /// This flag should only be used for contract deployments and nothing else. + /// @param to The callee of the call. + /// @param dataPtr The pointer to the calldata of the transaction. + function shouldMsgValueMimicCallBeSystem(to, dataPtr) -> ret { + let dataLen := mload(dataPtr) + // Note, that this point it is not fully known whether it is indeed the selector + // of the calldata (it might not be the case if the `dataLen` < 4), but it will be checked later on + let selector := shr(224, mload(add(dataPtr, 32))) + + let isSelectorCreate := or( + eq(selector, 0x9c4d535b), + eq(selector, 0xecf95b8a) + ) + let isSelectorCreate2 := or( + eq(selector, 0x3cda3351), + eq(selector, 0x5d382700) + ) + + // Firstly, ensure that the selector is a valid deployment function + ret := or( + isSelectorCreate, + isSelectorCreate2 + ) + // Secondly, ensure that the callee is ContractDeployer + ret := and(ret, eq(to, CONTRACT_DEPLOYER_ADDR())) + // Thirdly, ensure that the calldata is long enough to contain the selector + ret := and(ret, gt(dataLen, 3)) + } + + /// @dev Given the pointer to the calldata, the value and to + /// performs the call through the msg.value simulator. + /// @param to Which contract to call + /// @param from The `msg.sender` of the call. + /// @param value The `value` that will be used in the call. + /// @param dataPtr The pointer to the calldata of the transaction. It must store + /// the length of the calldata and the calldata itself right afterwards. + function msgValueSimulatorMimicCall(to, from, value, dataPtr) -> success { + // Only calls to the deployer system contract are allowed to be system + let isSystem := shouldMsgValueMimicCallBeSystem(to, dataPtr) + + success := mimicCallOnlyResult( + MSG_VALUE_SIMULATOR_ADDR(), + from, + dataPtr, + 0, + 1, + value, + to, + isSystem + ) + } + + /// @dev Checks whether the current frame has enough gas + /// @dev It does not use 63/64 rule and should only be called before nearCalls. + function checkEnoughGas(gasToProvide) { + debugLog("gas()", gas()) + debugLog("gasToProvide", gasToProvide) + + // Using margin of CHECK_ENOUGH_GAS_OVERHEAD gas to make sure that the operation will indeed + // have enough gas + if lt(gas(), safeAdd(gasToProvide, CHECK_ENOUGH_GAS_OVERHEAD(), "cjq")) { + revertWithReason(NOT_ENOUGH_GAS_PROVIDED_ERR_CODE(), 0) + } + } + + /// @dev This method returns the overhead that should be paid upfront by a transaction. + /// The goal of this overhead is to cover the possibility that this transaction may use up a certain + /// limited resource per batch: a single-instance circuit, etc. + /// The transaction needs to be able to pay the same % of the costs for publishing & proving the batch + /// as the % of the batch's limited resources that it can consume. + /// @param txEncodeLen The length of the ABI-encoding of the transaction + /// @dev The % following 2 resources is taken into account when calculating the % of the batch's overhead to pay. + /// 1. Overhead for taking up the bootloader memory. The bootloader memory has a cap on its length, mainly enforced to keep the RAM requirements + /// for the node smaller. That is, the user needs to pay a share proportional to the length of the ABI encoding of the transaction. + /// 2. Overhead for taking up a slot for the transaction. Since each batch has the limited number of transactions in it, the user must pay + /// at least 1/MAX_TRANSACTIONS_IN_BATCH part of the overhead. + function getTransactionUpfrontOverhead( + txEncodeLen + ) -> ret { + ret := max( + safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(), "iot"), + TX_SLOT_OVERHEAD_GAS() + ) + } + + /// @dev A method where all panics in the nearCalls get to. + /// It is needed to prevent nearCall panics from bubbling up. + function ZKSYNC_CATCH_NEAR_CALL() { + debugLog("ZKSYNC_CATCH_NEAR_CALL",0) + setHook(VM_HOOK_CATCH_NEAR_CALL()) + } + + /// @dev Prepends the selector before the txDataOffset, + /// preparing it to be used to call either `verify` or `execute`. + /// Returns the pointer to the calldata. + /// Note, that this overrides 32 bytes before the current transaction: + function prependSelector(txDataOffset, selector) -> ret { + + let calldataPtr := sub(txDataOffset, 4) + // Note, that since `mstore` stores 32 bytes at once, we need to + // actually store the selector in one word starting with the + // (txDataOffset - 32) = (calldataPtr - 28) + mstore(sub(calldataPtr, 28), selector) + + ret := calldataPtr + } + + /// @dev Returns the maximum of two numbers + function max(x, y) -> ret { + ret := y + if gt(x, y) { + ret := x + } + } + + /// @dev Returns the minimum of two numbers + function min(x, y) -> ret { + ret := y + if lt(x, y) { + ret := x + } + } + + /// @dev Returns constant that is equal to `keccak256("")` + function EMPTY_STRING_KECCAK() -> ret { + ret := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + } + + /// @dev Returns whether x <= y + function lte(x, y) -> ret { + ret := or(lt(x,y), eq(x,y)) + } + + /// @dev Checks whether an address is an account + /// @param addr The address to check + function ensureAccount(addr) { + mstore(0, 0xbb0fd61000000000000000000000000000000000000000000000000000000000) + mstore(4, addr) + + let success := call( + gas(), + CONTRACT_DEPLOYER_ADDR(), + 0, + 0, + 36, + 0, + 32 + ) + + let supportedVersion := mload(0) + + if iszero(success) { + revertWithReason( + FAILED_TO_CHECK_ACCOUNT_ERR_CODE(), + 1 + ) + } + + // This method returns AccountAbstractVersion enum. + // Currently only two versions are supported: 1 or 0, which basically + // mean whether the contract is an account or not. + if iszero(supportedVersion) { + revertWithReason( + FROM_IS_NOT_AN_ACCOUNT_ERR_CODE(), + 0 + ) + } + } + + /// @dev Checks whether an address is an EOA (i.e. has not code deployed on it) + /// @param addr The address to check + function isEOA(addr) -> ret { + ret := 0 + + if gt(addr, MAX_SYSTEM_CONTRACT_ADDR()) { + ret := iszero(getRawCodeHash(addr, false)) + } + } + + /// @dev Calls the `payForTransaction` method of an account + function accountPayForTx(account, txDataOffset) -> success { + success := callAccountMethod(0xe2f318e3, account, txDataOffset) + } + + /// @dev Calls the `prepareForPaymaster` method of an account + function accountPrePaymaster(account, txDataOffset) -> success { + success := callAccountMethod(0xa28c1aee, account, txDataOffset) + } + + /// @dev Calls the `validateAndPayForPaymasterTransaction` method of a paymaster + function validateAndPayForPaymasterTransaction(paymaster, txDataOffset) -> success { + success := callAccountMethod(0x038a24bc, paymaster, txDataOffset) + } + + /// @dev Used to call a method with the following signature; + /// someName( + /// bytes32 _txHash, + /// bytes32 _suggestedSignedHash, + /// Transaction calldata _transaction + /// ) + // Note, that this method expects that the current tx hashes are already stored + // in the `CURRENT_L2_TX_HASHES` slots. + function callAccountMethod(selector, account, txDataOffset) -> success { + // Safety invariant: it is safe to override data stored under + // `txDataOffset`, since the account methods are called only using + // `callAccountMethod` or `callPostOp` methods, both of which reformat + // the contents before innerTxDataOffset (i.e. txDataOffset + 32 bytes), + // i.e. make sure that the position at the txDataOffset has valid value. + let txDataWithHashesOffset := sub(txDataOffset, 64) + + // First word contains the canonical tx hash + let currentL2TxHashesPtr := CURRENT_L2_TX_HASHES_BEGIN_BYTE() + mstore(txDataWithHashesOffset, mload(currentL2TxHashesPtr)) + + // Second word contains the suggested tx hash for verifying + // signatures. + currentL2TxHashesPtr := add(currentL2TxHashesPtr, 32) + mstore(add(txDataWithHashesOffset, 32), mload(currentL2TxHashesPtr)) + + // Third word contains the offset of the main tx data (it is always 96 in our case) + mstore(add(txDataWithHashesOffset, 64), 96) + + let calldataPtr := prependSelector(txDataWithHashesOffset, selector) + let innerTxDataOffset := add(txDataOffset, 32) + + let len := getDataLength(innerTxDataOffset) + + // Besides the length of the transaction itself, + // we also require 3 words for hashes and the offset + // of the inner tx data. + let fullLen := add(len, 100) + + // The call itself. + success := call( + gas(), // The number of gas to pass. + account, // The address to call. + 0, // The `value` to pass. + calldataPtr, // The pointer to the calldata. + fullLen, // The size of the calldata, which is 4 for the selector + the actual length of the struct. + 0, // The pointer where the returned data will be written. + 0 // The output has size of 32 (a single bool is expected) + ) + } + + /// @dev Calculates and saves the explorer hash and the suggested signed hash for the transaction. + function saveTxHashes(txDataOffset) { + let calldataPtr := prependSelector(txDataOffset, 0xebe4a3d7) + let innerTxDataOffset := add(txDataOffset, 32) + + let len := getDataLength(innerTxDataOffset) + + // The first word is formal, but still required by the ABI + // We also should take into account the selector. + let fullLen := add(len, 36) + + // The call itself. + let success := call( + gas(), // The number of gas to pass. + BOOTLOADER_UTILITIES(), // The address to call. + 0, // The `value` to pass. + calldataPtr, // The pointer to the calldata. + fullLen, // The size of the calldata, which is 4 for the selector + the actual length of the struct. + CURRENT_L2_TX_HASHES_BEGIN_BYTE(), // The pointer where the returned data will be written. + 64 // The output has size of 32 (signed tx hash and explorer tx hash are expected) + ) + + if iszero(success) { + revertWithReason( + ACCOUNT_TX_VALIDATION_ERR_CODE(), + 1 + ) + } + + if iszero(eq(returndatasize(), 64)) { + assertionError("saveTxHashes: returndata invalid") + } + } + + /// @dev Encodes and calls the postOp method of the contract. + /// Note, that it *breaks* the contents of the previous transactions. + /// @param abi The near call ABI of the call + /// @param paymaster The address of the paymaster + /// @param txDataOffset The offset to the ABI-encoded Transaction struct. + /// @param txResult The status of the transaction (1 if succeeded, 0 otherwise). + /// @param maxRefundedGas The maximum number of gas the bootloader can be refunded. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// This is the `maximum` number because it does not take into account the number of gas that + /// can be spent by the paymaster itself. + function ZKSYNC_NEAR_CALL_callPostOp( + abi, + paymaster, + txDataOffset, + txResult, + maxRefundedGas, + basePubdataSpent, + gasPerPubdata, + reservedGas, + ) -> success { + // The postOp method has the following signature: + // function postTransaction( + // bytes calldata _context, + // Transaction calldata _transaction, + // bytes32 _txHash, + // bytes32 _suggestedSignedHash, + // ExecutionResult _txResult, + // uint256 _maxRefundedGas + // ) external payable; + // The encoding is the following: + // 1. Offset to the _context's content. (32 bytes) + // 2. Offset to the _transaction's content. (32 bytes) + // 3. _txHash (32 bytes) + // 4. _suggestedSignedHash (32 bytes) + // 5. _txResult (32 bytes) + // 6. _maxRefundedGas (32 bytes) + // 7. _context (note, that the content must be padded to 32 bytes) + // 8. _transaction + + let contextLen := mload(PAYMASTER_CONTEXT_BEGIN_BYTE()) + let paddedContextLen := lengthRoundedByWords(contextLen) + // The length of selector + the first 7 fields (with context len) + context itself. + let preTxLen := add(228, paddedContextLen) + + let innerTxDataOffset := add(txDataOffset, 32) + let calldataPtr := sub(innerTxDataOffset, preTxLen) + + { + let ptr := calldataPtr + + // Selector + mstore(ptr, 0x817b17f000000000000000000000000000000000000000000000000000000000) + ptr := add(ptr, 4) + + // context ptr + mstore(ptr, 192) // The context always starts at 32 * 6 position + ptr := add(ptr, 32) + + // transaction ptr + mstore(ptr, sub(innerTxDataOffset, add(calldataPtr, 4))) + ptr := add(ptr, 32) + + // tx hash + mstore(ptr, mload(CURRENT_L2_TX_HASHES_BEGIN_BYTE())) + ptr := add(ptr, 32) + + // suggested signed hash + mstore(ptr, mload(add(CURRENT_L2_TX_HASHES_BEGIN_BYTE(), 32))) + ptr := add(ptr, 32) + + // tx result + mstore(ptr, txResult) + ptr := add(ptr, 32) + + // maximal refunded gas + mstore(ptr, maxRefundedGas) + ptr := add(ptr, 32) + + // storing context itself + memCopy(PAYMASTER_CONTEXT_BEGIN_BYTE(), ptr, add(32, paddedContextLen)) + ptr := add(ptr, add(32, paddedContextLen)) + + // At this point, the ptr should reach the innerTxDataOffset. + // If not, we have done something wrong here. + if iszero(eq(ptr, innerTxDataOffset)) { + assertionError("postOp: ptr != innerTxDataOffset") + } + + // no need to store the transaction as from the innerTxDataOffset starts + // valid encoding of the transaction + } + + let calldataLen := safeAdd(preTxLen, getDataLength(innerTxDataOffset), "jiq") + + success := call( + gas(), + paymaster, + 0, + calldataPtr, + calldataLen, + 0, + 0 + ) + + if isNotEnoughGasForPubdata( + basePubdataSpent, + gas(), + reservedGas, + gasPerPubdata, + ) { + // If not enough gas for pubdata was provided, we revert all the state diffs / messages + // that caused the pubdata to be published + nearCallPanic() + } + } + + /// @dev Copies [from..from+len] to [to..to+len] + /// Note, that len must be divisible by 32. + function memCopy(from, to, len) { + // Ensuring that len is always divisible by 32. + if mod(len, 32) { + assertionError("Memcopy with unaligned length") + } + + let finalFrom := safeAdd(from, len, "cka") + + for { } lt(from, finalFrom) { + from := add(from, 32) + to := add(to, 32) + } { + mstore(to, mload(from)) + } + } + + /// @dev Validates the transaction against the senders' account. + /// Besides ensuring that the contract agrees to a transaction, + /// this method also enforces that the nonce has been marked as used. + function accountValidateTx(txDataOffset) { + // Skipping the first 0x20 word of the ABI-encoding of the struct + let innerTxDataOffset := add(txDataOffset, 32) + let from := getFrom(innerTxDataOffset) + ensureAccount(from) + + // The nonce should be unique for each transaction. + let nonce := getNonce(innerTxDataOffset) + // Here we check that this nonce was not available before the validation step + ensureNonceUsage(from, nonce, 0) + + setHook(VM_HOOK_ACCOUNT_VALIDATION_ENTERED()) + debugLog("pre-validate",0) + debugLog("pre-validate",from) + let success := callAccountMethod(0x202bcce7, from, txDataOffset) + setHook(VM_HOOK_NO_VALIDATION_ENTERED()) + + if iszero(success) { + revertWithReason( + ACCOUNT_TX_VALIDATION_ERR_CODE(), + 1 + ) + } + + ensureCorrectAccountMagic() + + // Here we make sure that the nonce is no longer available after the validation step + ensureNonceUsage(from, nonce, 1) + } + + /// @dev Ensures that the magic returned by the validate account method is correct + /// It must be called right after the call of the account validation method to preserve the + /// correct returndatasize + function ensureCorrectAccountMagic() { + // It is expected that the returned value is ABI-encoded bytes4 magic value + // The Solidity always pads such value to 32 bytes and so we expect the magic to be + // of length 32 + if iszero(eq(32, returndatasize())) { + revertWithReason( + ACCOUNT_RETURNED_INVALID_MAGIC_ERR_CODE(), + 0 + ) + } + + // Note that it is important to copy the magic even though it is not needed if the + // `SHOULD_ENSURE_CORRECT_RETURNED_MAGIC` is false. It is never false in production + // but it is so in fee estimation and we want to preserve as many operations as + // in the original operation. + returndatacopy(0, 0, 32) + let returnedValue := mload(0) + let isMagicCorrect := eq(returnedValue, 0x202bcce700000000000000000000000000000000000000000000000000000000) + + if and(iszero(isMagicCorrect), SHOULD_ENSURE_CORRECT_RETURNED_MAGIC()) { + revertWithReason( + ACCOUNT_RETURNED_INVALID_MAGIC_ERR_CODE(), + 0 + ) + } + } + + /// @dev Calls the KnownCodesStorage system contract to mark the factory dependencies of + /// the transaction as known. + function markFactoryDepsForTx(innerTxDataOffset, isL1Tx) { + debugLog("starting factory deps", 0) + let factoryDepsPtr := getFactoryDepsPtr(innerTxDataOffset) + let factoryDepsLength := mload(factoryDepsPtr) + + if gt(factoryDepsLength, MAX_NEW_FACTORY_DEPS()) { + assertionError("too many factory deps") + } + + let ptr := NEW_FACTORY_DEPS_BEGIN_BYTE() + // Selector + mstore(ptr, 0xe516761e) + ptr := add(ptr, 32) + + // Saving whether the dependencies should be sent on L1 + // There is no need to send them for L1 transactions, since their + // preimages are already available on L1. + mstore(ptr, iszero(isL1Tx)) + ptr := add(ptr, 32) + + // Saving the offset to array (it is always 64) + mstore(ptr, 64) + ptr := add(ptr, 32) + + // Saving the array + + // We also need to include 32 bytes for the length itself + let arrayLengthBytes := safeAdd(32, safeMul(factoryDepsLength, 32, "ag"), "af") + // Copying factory deps array + memCopy(factoryDepsPtr, ptr, arrayLengthBytes) + + let success := call( + gas(), + KNOWN_CODES_CONTRACT_ADDR(), + 0, + // Shifting by 28 to start from the selector + add(NEW_FACTORY_DEPS_BEGIN_BYTE(), 28), + // 4 (selector) + 32 (send to l1 flag) + 32 (factory deps offset)+ 32 (factory deps length) + safeAdd(100, safeMul(factoryDepsLength, 32, "op"), "ae"), + 0, + 0 + ) + + debugLog("factory deps success", success) + + if iszero(success) { + debugReturndata() + switch isL1Tx + case 1 { + revertWithReason( + FAILED_TO_MARK_FACTORY_DEPS(), + 1 + ) + } + default { + // For L2 transactions, we use near call panic + nearCallPanic() + } + } + } + + /// @dev Function responsible for executing the L1->L2 transactions. + function executeL1Tx(innerTxDataOffset, from) -> ret { + let to := getTo(innerTxDataOffset) + debugLog("to", to) + let value := getValue(innerTxDataOffset) + debugLog("value", value) + let dataPtr := getDataPtr(innerTxDataOffset) + + ret := msgValueSimulatorMimicCall( + to, + from, + value, + dataPtr + ) + + if iszero(ret) { + debugReturndata() + } + } + + /// @dev Function responsible for the execution of the L2 transaction + /// @dev Returns `true` or `false` depending on whether or not the tx has reverted. + function executeL2Tx(txDataOffset, from) -> ret { + ret := callAccountMethod(0xdf9c1589, from, txDataOffset) + + if iszero(ret) { + debugReturndata() + } + } + + /// + /// zkSync-specific utilities: + /// + + /// @dev Returns an ABI that can be used for low-level + /// invocations of calls and mimicCalls + /// @param dataPtr The pointer to the calldata. + /// @param gasPassed The number of gas to be passed with the call. + /// @param shardId The shard id of the callee. Currently only `0` (Rollup) is supported. + /// @param forwardingMode The mode of how the calldata is forwarded + /// It is possible to either pass a pointer, slice of auxheap or heap. For the + /// bootloader purposes using heap (0) is enough. + /// @param isConstructorCall Whether the call should contain the isConstructor flag. + /// @param isSystemCall Whether the call should contain the isSystemCall flag. + /// @return ret The ABI + function getFarCallABI( + dataPtr, + gasPassed, + shardId, + forwardingMode, + isConstructorCall, + isSystemCall + ) -> ret { + let dataStart := add(dataPtr, 32) + let dataLength := mload(dataPtr) + + // Skip dataOffset and memoryPage, because they are always zeros + ret := or(ret, shl(64, dataStart)) + ret := or(ret, shl(96, dataLength)) + + ret := or(ret, shl(192, gasPassed)) + ret := or(ret, shl(224, forwardingMode)) + ret := or(ret, shl(232, shardId)) + ret := or(ret, shl(240, isConstructorCall)) + ret := or(ret, shl(248, isSystemCall)) + } + + /// @dev Does mimicCall without copying the returndata. + /// @param to Who to call + /// @param whoToMimic The `msg.sender` of the call + /// @param data The pointer to the calldata + /// @param isConstructor Whether the call should contain the isConstructor flag + /// @param isSystemCall Whether the call should contain the isSystem flag. + /// @param extraAbi1 The first extraAbiParam + /// @param extraAbi2 The second extraAbiParam + /// @param extraAbi3 The third extraAbiParam + /// @return ret 1 if the call was successful, 0 otherwise. + function mimicCallOnlyResult( + to, + whoToMimic, + data, + isConstructor, + isSystemCall, + extraAbi1, + extraAbi2, + extraAbi3 + ) -> ret { + let farCallAbi := getFarCallABI( + data, + gas(), + // Only rollup is supported for now + 0, + 0, + isConstructor, + isSystemCall + ) + + ret := verbatim_7i_1o("system_mimic_call", to, whoToMimic, farCallAbi, extraAbi1, extraAbi2, extraAbi3, 0) + } + + + + /// @dev Sends a L2->L1 log using L1Messengers' `sendL2ToL1Log`. + /// @param isService The isService flag of the call. + /// @param key The `key` parameter of the log. + /// @param value The `value` parameter of the log. + function sendL2LogUsingL1Messenger(isService, key, value) { + mstore(0, 0x56079ac800000000000000000000000000000000000000000000000000000000) + mstore(4, isService) + mstore(36, key) + mstore(68, value) + + let success := call( + gas(), + L1_MESSENGER_ADDR(), + 0, + 0, + 100, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed to send L1Messenger L2Log", key) + debugLog("Failed to send L1Messenger L2Log", value) + + revertWithReason(L1_MESSENGER_LOG_SENDING_FAILED_ERR_CODE(), 1) + } + } + + /// @dev Sends a native (VM) L2->L1 log. + /// @param isService The isService flag of the call. + /// @param key The `key` parameter of the log. + /// @param value The `value` parameter of the log. + function sendToL1Native(isService, key, value) { + verbatim_3i_0o("to_l1", isService, key, value) + } + + /// @notice Performs L1 Messenger pubdata "publishing" call. + /// @dev Expected to be used at the end of the batch. + function l1MessengerPublishingCall() { + let ptr := OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_BEGIN_BYTE() + debugLog("Publishing batch data to L1", 0) + // First slot (only last 4 bytes) -- selector + mstore(ptr, 0x628b636e) + // Second slot -- offset + mstore(add(ptr, 32), 32) + setHook(VM_HOOK_PUBDATA_REQUESTED()) + // Third slot -- length of pubdata + let len := mload(add(ptr, 64)) + // 4 bytes for selector, 32 bytes for array offset and 32 bytes for array length + let fullLen := add(len, 68) + + // ptr + 28 because the function selector only takes up the last 4 bytes in the first slot. + let success := call( + gas(), + L1_MESSENGER_ADDR(), + 0, + add(ptr, 28), + fullLen, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed to publish L2Logs data", 0) + + revertWithReason(L1_MESSENGER_PUBLISHING_FAILED_ERR_CODE(), 1) + } + } + + function publishTimestampDataToL1() { + debugLog("Publishing timestamp data to L1", 0) + + mstore(0, 0x7c9bd1f300000000000000000000000000000000000000000000000000000000) + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 4, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed publish timestamp to L1", 0) + revertWithReason(FAILED_TO_PUBLISH_TIMESTAMP_DATA_TO_L1(), 1) + } + } + + /// @notice Performs a call of a System Context + /// method that have no input parameters + function callSystemContext(paddedSelector) { + mstore(0, paddedSelector) + + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 4, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed to call System Context", 0) + + revertWithReason(FAILED_TO_CALL_SYSTEM_CONTEXT_ERR_CODE(), 1) + } + } + + /// @dev Increment the number of txs in the batch + function considerNewTx() { + verbatim_0i_0o("increment_tx_counter") + + callSystemContext(0x30e5ccbd00000000000000000000000000000000000000000000000000000000) + } + + function $llvm_NoInline_llvm$_getMeta() -> ret { + ret := verbatim_0i_1o("meta") + } + + function getPubdataCounter() -> ret { + ret := and($llvm_NoInline_llvm$_getMeta(), 0xFFFFFFFF) + } + + function getCurrentPubdataSpent(basePubdataSpent) -> ret { + let currentPubdataCounter := getPubdataCounter() + debugLog("basePubdata", basePubdataSpent) + debugLog("currentPubdata", currentPubdataCounter) + ret := saturatingSub(currentPubdataCounter, basePubdataSpent) + } + + function getErgsSpentForPubdata( + basePubdataSpent, + gasPerPubdata, + ) -> ret { + ret := safeMul(getCurrentPubdataSpent(basePubdataSpent), gasPerPubdata, "mul: getErgsSpentForPubdata") + } + + /// @dev Compares the amount of spent ergs on the pubdatawith the allowed amount. + /// @param basePubdataSpent The amount of pubdata spent at the beginning of the transaction. + /// @param computeGas The amount of execution gas remaining that can still be spent on future computation. + /// @param reservedGas The amount of gas reserved for the pubdata. + /// @param gasPerPubdata The price of each byte of pubdata in L2 gas. + /// @return ret Whether the amount of pubdata spent so far is valid and + /// and can be covered by the user. + function isNotEnoughGasForPubdata( + basePubdataSpent, + computeGas, + reservedGas, + gasPerPubdata + ) -> ret { + let spentErgs := getErgsSpentForPubdata(basePubdataSpent, gasPerPubdata) + debugLog("spentErgsPubdata", spentErgs) + let allowedGasLimit := add(computeGas, reservedGas) + + ret := lt(allowedGasLimit, spentErgs) + } + + /// @dev Set the new value for the tx origin context value + function setTxOrigin(newTxOrigin) { + let success := setContextVal(0xa851ae7800000000000000000000000000000000000000000000000000000000, newTxOrigin) + + if iszero(success) { + debugLog("Failed to set txOrigin", newTxOrigin) + nearCallPanic() + } + } + + /// @dev Set the new value for the gas price value + function setGasPrice(newGasPrice) { + let success := setContextVal(0xbf1fe42000000000000000000000000000000000000000000000000000000000, newGasPrice) + + if iszero(success) { + debugLog("Failed to set gas price", newGasPrice) + nearCallPanic() + } + } + + /// @dev Sets the gas per pubdata byte value in the `SystemContext` contract. + /// @param newGasPerPubdata The amount L2 gas that the operator charge the user for single byte of pubdata. + /// @param basePubdataSpent The number of pubdata spent as of the start of the transaction. + /// @notice Note that it has no actual impact on the execution of the contract. + function setPubdataInfo( + newGasPerPubdata, + basePubdataSpent + ) { + mstore(0, 0xa225efcb00000000000000000000000000000000000000000000000000000000) + mstore(4, newGasPerPubdata) + mstore(36, basePubdataSpent) + + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 68, + 0, + 0 + ) + + if iszero(success) { + debugLog("setPubdataInfo failed", newGasPerPubdata) + assertionError("setPubdataInfo failed") + } + } + + /// @notice Sets the context information for the current batch. + /// @dev The SystemContext.sol system contract is responsible for validating + /// the validity of the new batch's data. + function setNewBatch(prevBatchHash, newTimestamp, newBatchNumber, baseFee) { + mstore(0, 0x02fa577900000000000000000000000000000000000000000000000000000000) + mstore(4, prevBatchHash) + mstore(36, newTimestamp) + mstore(68, newBatchNumber) + mstore(100, baseFee) + + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 132, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed to set new batch: ", prevBatchHash) + debugLog("Failed to set new batch: ", newTimestamp) + + revertWithReason(FAILED_TO_SET_NEW_BATCH_ERR_CODE(), 1) + } + } + + /// @notice Sets the context information for the current L2 block. + /// @param txId The index of the transaction in the batch for which to get the L2 block information. + function setL2Block(txId) { + let txL2BlockPosition := add(TX_OPERATOR_L2_BLOCK_INFO_BEGIN_BYTE(), mul(TX_OPERATOR_L2_BLOCK_INFO_SIZE_BYTES(), txId)) + + let currentL2BlockNumber := mload(txL2BlockPosition) + let currentL2BlockTimestamp := mload(add(txL2BlockPosition, 32)) + let previousL2BlockHash := mload(add(txL2BlockPosition, 64)) + let virtualBlocksToCreate := mload(add(txL2BlockPosition, 96)) + + let isFirstInBatch := iszero(txId) + + debugLog("Setting new L2 block: ", currentL2BlockNumber) + debugLog("Setting new L2 block: ", currentL2BlockTimestamp) + debugLog("Setting new L2 block: ", previousL2BlockHash) + debugLog("Setting new L2 block: ", virtualBlocksToCreate) + + mstore(0, 0x06bed03600000000000000000000000000000000000000000000000000000000) + mstore(4, currentL2BlockNumber) + mstore(36, currentL2BlockTimestamp) + mstore(68, previousL2BlockHash) + mstore(100, isFirstInBatch) + mstore(132, virtualBlocksToCreate) + + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 164, + 0, + 0 + ) + + if iszero(success) { + debugLog("Failed to set new L2 block: ", currentL2BlockNumber) + debugLog("Failed to set new L2 block: ", currentL2BlockTimestamp) + debugLog("Failed to set new L2 block: ", previousL2BlockHash) + debugLog("Failed to set new L2 block: ", isFirstInBatch) + + revertWithReason(FAILED_TO_SET_L2_BLOCK(), 1) + } + } + + /// @notice Appends the transaction hash to the current L2 block. + /// @param txHash The hash of the transaction to append. + /// @param isL1Tx Whether the transaction is an L1 transaction. If it is an L1 transaction, + /// and this method fails, then the bootloader execution will be explicitly reverted. + /// Otherwise, the nearCallPanic will be used to implicitly fail the validation of the transaction. + function appendTransactionHash( + txHash, + isL1Tx + ) { + debugLog("Appending tx to L2 block", txHash) + + mstore(0, 0x06e7517b00000000000000000000000000000000000000000000000000000000) + mstore(4, txHash) + + let success := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 36, + 0, + 0 + ) + + if iszero(success) { + debugReturndata() + switch isL1Tx + case 1 { + revertWithReason( + FAILED_TO_APPEND_TRANSACTION_TO_L2_BLOCK(), + 1 + ) + } + default { + // For L2 transactions, we use near call panic, it will trigger the validation + // step of the transaction to fail, returning a consistent error message. + nearCallPanic() + } + } + } + + + + // Checks whether the nonce `nonce` have been already used for + // account `from`. Reverts if the nonce has not been used properly. + function ensureNonceUsage(from, nonce, shouldNonceBeUsed) { + // INonceHolder.validateNonceUsage selector + mstore(0, 0x6ee1dc2000000000000000000000000000000000000000000000000000000000) + mstore(4, from) + mstore(36, nonce) + mstore(68, shouldNonceBeUsed) + + let success := call( + gas(), + NONCE_HOLDER_ADDR(), + 0, + 0, + 100, + 0, + 0 + ) + + if iszero(success) { + revertWithReason( + ACCOUNT_TX_VALIDATION_ERR_CODE(), + 1 + ) + } + } + + /// @dev Encodes and performs a call to a method of + /// `SystemContext.sol` system contract of the roughly the following interface: + /// someMethod(uint256 val) + function setContextVal( + selector, + value, + ) -> ret { + mstore(0, selector) + mstore(4, value) + + ret := call( + gas(), + SYSTEM_CONTEXT_ADDR(), + 0, + 0, + 36, + 0, + 0 + ) + } + + // Each of the txs have the following type: + // struct Transaction { + // // The type of the transaction. + // uint256 txType; + // // The caller. + // uint256 from; + // // The callee. + // uint256 to; + // // The gasLimit to pass with the transaction. + // // It has the same meaning as Ethereum's gasLimit. + // uint256 gasLimit; + // // The maximum amount of gas the user is willing to pay for a byte of pubdata. + // uint256 gasPerPubdataByteLimit; + // // The maximum fee per gas that the user is willing to pay. + // // It is akin to EIP1559's maxFeePerGas. + // uint256 maxFeePerGas; + // // The maximum priority fee per gas that the user is willing to pay. + // // It is akin to EIP1559's maxPriorityFeePerGas. + // uint256 maxPriorityFeePerGas; + // // The transaction's paymaster. If there is no paymaster, it is equal to 0. + // uint256 paymaster; + // // The nonce of the transaction. + // uint256 nonce; + // // The value to pass with the transaction. + // uint256 value; + // // In the future, we might want to add some + // // new fields to the struct. The `txData` struct + // // is to be passed to account and any changes to its structure + // // would mean a breaking change to these accounts. In order to prevent this, + // // we should keep some fields as "reserved". + // // It is also recommended that their length is fixed, since + // // it would allow easier proof integration (in case we will need + // // some special circuit for preprocessing transactions). + // uint256[4] reserved; + // // The transaction's calldata. + // bytes data; + // // The signature of the transaction. + // bytes signature; + // // The properly formatted hashes of bytecodes that must be published on L1 + // // with the inclusion of this transaction. Note, that a bytecode has been published + // // before, the user won't pay fees for its republishing. + // bytes32[] factoryDeps; + // // The input to the paymaster. + // bytes paymasterInput; + // // Reserved dynamic type for the future use-case. Using it should be avoided, + // // But it is still here, just in case we want to enable some additional functionality. + // bytes reservedDynamic; + // } + + /// @notice Asserts the equality of two values and reverts + /// with the appropriate error message in case it doesn't hold + /// @param value1 The first value of the assertion + /// @param value2 The second value of the assertion + /// @param message The error message + function assertEq(value1, value2, message) { + switch eq(value1, value2) + case 0 { assertionError(message) } + default { } + } + + /// @notice Makes sure that the structure of the transaction is set in accordance to its type + /// @dev This function validates only L2 transactions, since the integrity of the L1->L2 + /// transactions is enforced by the L1 smart contracts. + function validateTypedTxStructure(innerTxDataOffset) { + /// Some common checks for all transactions. + let reservedDynamicLength := getReservedDynamicBytesLength(innerTxDataOffset) + if gt(reservedDynamicLength, 0) { + assertionError("non-empty reservedDynamic") + } + let txType := getTxType(innerTxDataOffset) + switch txType + case 0 { + let maxFeePerGas := getMaxFeePerGas(innerTxDataOffset) + let maxPriorityFeePerGas := getMaxPriorityFeePerGas(innerTxDataOffset) + assertEq(maxFeePerGas, maxPriorityFeePerGas, "EIP1559 params wrong") + + let from := getFrom(innerTxDataOffset) + let iseoa := isEOA(from) + assertEq(iseoa, true, "Only EIP-712 can use non-EOA") + + + // Here, for type 0 transactions the reserved0 field is used as a marker + // whether the transaction should include chainId in its encoding. + assertEq(lte(getGasPerPubdataByteLimit(innerTxDataOffset), MAX_L2_GAS_PER_PUBDATA()), 1, "Gas per pubdata is wrong") + assertEq(getPaymaster(innerTxDataOffset), 0, "paymaster non zero") + + assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") + + 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") + assertEq(getPaymasterInputBytesLength(innerTxDataOffset), 0, "paymasterInput non zero") + } + case 1 { + let maxFeePerGas := getMaxFeePerGas(innerTxDataOffset) + let maxPriorityFeePerGas := getMaxPriorityFeePerGas(innerTxDataOffset) + assertEq(maxFeePerGas, maxPriorityFeePerGas, "EIP1559 params wrong") + + let from := getFrom(innerTxDataOffset) + let iseoa := isEOA(from) + assertEq(iseoa, true, "Only EIP-712 can use non-EOA") + + + assertEq(lte(getGasPerPubdataByteLimit(innerTxDataOffset), MAX_L2_GAS_PER_PUBDATA()), 1, "Gas per pubdata is wrong") + assertEq(getPaymaster(innerTxDataOffset), 0, "paymaster non zero") + + 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(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") + assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") + assertEq(getFactoryDepsBytesLength(innerTxDataOffset), 0, "factory deps non zero") + assertEq(getPaymasterInputBytesLength(innerTxDataOffset), 0, "paymasterInput non zero") + } + case 2 { + assertEq(lte(getGasPerPubdataByteLimit(innerTxDataOffset), MAX_L2_GAS_PER_PUBDATA()), 1, "Gas per pubdata is wrong") + assertEq(getPaymaster(innerTxDataOffset), 0, "paymaster non zero") + + let from := getFrom(innerTxDataOffset) + let iseoa := isEOA(from) + assertEq(iseoa, true, "Only EIP-712 can use non-EOA") + + + 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(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") + assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") + assertEq(getFactoryDepsBytesLength(innerTxDataOffset), 0, "factory deps non zero") + assertEq(getPaymasterInputBytesLength(innerTxDataOffset), 0, "paymasterInput non zero") + } + case 113 { + let paymaster := getPaymaster(innerTxDataOffset) + assertEq(or(gt(paymaster, MAX_SYSTEM_CONTRACT_ADDR()), iszero(paymaster)), 1, "paymaster in kernel space") + + if iszero(paymaster) { + // Double checking that the paymasterInput is 0 if the paymaster is 0 + assertEq(getPaymasterInputBytesLength(innerTxDataOffset), 0, "paymasterInput non zero") + } + + 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(getReserved2(innerTxDataOffset), 0, "reserved2 non zero") + assertEq(getReserved3(innerTxDataOffset), 0, "reserved3 non zero") + } + case 254 { + // Upgrade transaction, no need to validate as it is validated on L1. + } + case 255 { + // Double-check that the operator doesn't try to do an upgrade transaction via L1 -> L2 transaction. + assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") + // L1 transaction, no need to validate as it is validated on L1. + } + default { + assertionError("Unknown tx type") + } + } + + /// + /// TransactionData utilities + /// + /// @dev The next methods are programmatically generated + /// + + function getTxType(innerTxDataOffset) -> ret { + ret := mload(innerTxDataOffset) + } + + function getFrom(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 32)) + } + + function getTo(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 64)) + } + + function getGasLimit(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 96)) + } + + function getGasPerPubdataByteLimit(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 128)) + } + + function getMaxFeePerGas(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 160)) + } + + function getMaxPriorityFeePerGas(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 192)) + } + + function getPaymaster(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 224)) + } + + function getNonce(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 256)) + } + + function getValue(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 288)) + } + + function getReserved0(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 320)) + } + + function getReserved1(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 352)) + } + + function getReserved2(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 384)) + } + + function getReserved3(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 416)) + } + + function getDataPtr(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 448)) + ret := add(innerTxDataOffset, ret) + } + + function getDataBytesLength(innerTxDataOffset) -> ret { + let ptr := getDataPtr(innerTxDataOffset) + ret := lengthRoundedByWords(mload(ptr)) + } + + function getSignaturePtr(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 480)) + ret := add(innerTxDataOffset, ret) + } + + function getSignatureBytesLength(innerTxDataOffset) -> ret { + let ptr := getSignaturePtr(innerTxDataOffset) + ret := lengthRoundedByWords(mload(ptr)) + } + + function getFactoryDepsPtr(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 512)) + ret := add(innerTxDataOffset, ret) + } + + function getFactoryDepsBytesLength(innerTxDataOffset) -> ret { + let ptr := getFactoryDepsPtr(innerTxDataOffset) + ret := safeMul(mload(ptr),32, "fwop") + } + + function getPaymasterInputPtr(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 544)) + ret := add(innerTxDataOffset, ret) + } + + function getPaymasterInputBytesLength(innerTxDataOffset) -> ret { + let ptr := getPaymasterInputPtr(innerTxDataOffset) + ret := lengthRoundedByWords(mload(ptr)) + } + + function getReservedDynamicPtr(innerTxDataOffset) -> ret { + ret := mload(add(innerTxDataOffset, 576)) + ret := add(innerTxDataOffset, ret) + } + + function getReservedDynamicBytesLength(innerTxDataOffset) -> ret { + let ptr := getReservedDynamicPtr(innerTxDataOffset) + ret := lengthRoundedByWords(mload(ptr)) + } + + /// This method checks that the transaction's structure is correct + /// and tightly packed + function validateAbiEncoding(txDataOffset) -> ret { + if iszero(eq(mload(txDataOffset), 32)) { + assertionError("Encoding offset") + } + + let innerTxDataOffset := add(txDataOffset, 32) + + let fromValue := getFrom(innerTxDataOffset) + if iszero(validateAddress(fromValue)) { + assertionError("Encoding from") + } + + let toValue := getTo(innerTxDataOffset) + if iszero(validateAddress(toValue)) { + assertionError("Encoding to") + } + + let gasLimitValue := getGasLimit(innerTxDataOffset) + if iszero(validateUint64(gasLimitValue)) { + assertionError("Encoding gasLimit") + } + + let gasPerPubdataByteLimitValue := getGasPerPubdataByteLimit(innerTxDataOffset) + if iszero(validateUint32(gasPerPubdataByteLimitValue)) { + assertionError("Encoding gasPerPubdataByteLimit") + } + + let maxFeePerGas := getMaxFeePerGas(innerTxDataOffset) + if iszero(validateUint128(maxFeePerGas)) { + assertionError("Encoding maxFeePerGas") + } + + let maxPriorityFeePerGas := getMaxPriorityFeePerGas(innerTxDataOffset) + if iszero(validateUint128(maxPriorityFeePerGas)) { + assertionError("Encoding maxPriorityFeePerGas") + } + + let paymasterValue := getPaymaster(innerTxDataOffset) + if iszero(validateAddress(paymasterValue)) { + assertionError("Encoding paymaster") + } + + let expectedDynamicLenPtr := add(innerTxDataOffset, 608) + + let dataLengthPos := getDataPtr(innerTxDataOffset) + if iszero(eq(dataLengthPos, expectedDynamicLenPtr)) { + assertionError("Encoding data") + } + expectedDynamicLenPtr := validateBytes(dataLengthPos) + + let signatureLengthPos := getSignaturePtr(innerTxDataOffset) + if iszero(eq(signatureLengthPos, expectedDynamicLenPtr)) { + assertionError("Encoding signature") + } + expectedDynamicLenPtr := validateBytes(signatureLengthPos) + + let factoryDepsLengthPos := getFactoryDepsPtr(innerTxDataOffset) + if iszero(eq(factoryDepsLengthPos, expectedDynamicLenPtr)) { + assertionError("Encoding factoryDeps") + } + expectedDynamicLenPtr := validateBytes32Array(factoryDepsLengthPos) + + let paymasterInputLengthPos := getPaymasterInputPtr(innerTxDataOffset) + if iszero(eq(paymasterInputLengthPos, expectedDynamicLenPtr)) { + assertionError("Encoding paymasterInput") + } + expectedDynamicLenPtr := validateBytes(paymasterInputLengthPos) + + let reservedDynamicLengthPos := getReservedDynamicPtr(innerTxDataOffset) + if iszero(eq(reservedDynamicLengthPos, expectedDynamicLenPtr)) { + assertionError("Encoding reservedDynamic") + } + expectedDynamicLenPtr := validateBytes(reservedDynamicLengthPos) + + ret := expectedDynamicLenPtr + } + + function getDataLength(innerTxDataOffset) -> ret { + // To get the length of the txData in bytes, we can simply + // get the number of fields * 32 + the length of the dynamic types + // in bytes. + ret := 768 + + ret := safeAdd(ret, getDataBytesLength(innerTxDataOffset), "asx") + ret := safeAdd(ret, getSignatureBytesLength(innerTxDataOffset), "qwqa") + ret := safeAdd(ret, getFactoryDepsBytesLength(innerTxDataOffset), "sic") + ret := safeAdd(ret, getPaymasterInputBytesLength(innerTxDataOffset), "tpiw") + ret := safeAdd(ret, getReservedDynamicBytesLength(innerTxDataOffset), "shy") + } + + /// + /// End of programmatically generated code + /// + + /// @dev Accepts an address and returns whether or not it is + /// a valid address + function validateAddress(addr) -> ret { + ret := lt(addr, shl(160, 1)) + } + + /// @dev Accepts an uint32 and returns whether or not it is + /// a valid uint32 + function validateUint32(x) -> ret { + ret := lt(x, shl(32,1)) + } + + /// @dev Accepts an uint64 and returns whether or not it is + /// a valid uint64 + function validateUint64(x) -> ret { + ret := lt(x, shl(64,1)) + } + + /// @dev Accepts an uint128 and returns whether or not it is + /// a valid uint128 + function validateUint128(x) -> ret { + ret := lt(x, shl(128,1)) + } + + /// Validates that the `bytes` is formed correctly + /// and returns the pointer right after the end of the bytes + function validateBytes(bytesPtr) -> bytesEnd { + let length := mload(bytesPtr) + let lastWordBytes := mod(length, 32) + + switch lastWordBytes + case 0 { + // If the length is divisible by 32, then + // the bytes occupy whole words, so there is + // nothing to validate + bytesEnd := safeAdd(bytesPtr, safeAdd(length, 32, "pol"), "aop") + } + default { + // If the length is not divisible by 32, then + // the last word is padded with zeroes, i.e. + // the last 32 - `lastWordBytes` bytes must be zeroes + // The easiest way to check this is to use AND operator + + let zeroBytes := sub(32, lastWordBytes) + // It has its first 32 - `lastWordBytes` bytes set to 255 + let mask := sub(shl(mul(zeroBytes,8),1),1) + + let fullLen := lengthRoundedByWords(length) + bytesEnd := safeAdd(bytesPtr, safeAdd(32, fullLen, "dza"), "dzp") + + let lastWord := mload(sub(bytesEnd, 32)) + + // If last word contains some unintended bits + // return 0 + if and(lastWord, mask) { + assertionError("bad bytes encoding") + } + } + } + + /// @dev Accepts the pointer to the bytes32[] array length and + /// returns the pointer right after the array's content + function validateBytes32Array(arrayPtr) -> arrayEnd { + // The bytes32[] array takes full words which may contain any content. + // Thus, there is nothing to validate. + let length := mload(arrayPtr) + arrayEnd := safeAdd(arrayPtr, safeAdd(32, safeMul(length, 32, "lop"), "asa"), "sp") + } + + /// + /// Safe math utilities + /// + + /// @dev Returns the multiplication of two unsigned integers, reverting on overflow. + function safeMul(x, y, errMsg) -> ret { + switch y + case 0 { + ret := 0 + } + default { + ret := mul(x, y) + if iszero(eq(div(ret, y), x)) { + assertionError(errMsg) + } + } + } + + /// @dev Returns the integer division of two unsigned integers. Reverts with custom message on + /// division by zero. The result is rounded towards zero. + function safeDiv(x, y, errMsg) -> ret { + if iszero(y) { + assertionError(errMsg) + } + ret := div(x, y) + } + + /// @dev Returns the addition of two unsigned integers, reverting on overflow. + function safeAdd(x, y, errMsg) -> ret { + ret := add(x, y) + if lt(ret, x) { + assertionError(errMsg) + } + } + + /// @dev Returns the subtraction of two unsigned integers, reverting on underflow. + function safeSub(x, y, errMsg) -> ret { + if gt(y, x) { + assertionError(errMsg) + } + ret := sub(x, y) + } + + function saturatingSub(x, y) -> ret { + switch gt(x,y) + case 0 { + ret := 0 + } + default { + ret := sub(x,y) + } + } + + /// + /// Debug utilities + /// + + /// @dev This method accepts the message and some 1-word data associated with it + /// It triggers a VM hook that allows the server to observe the behavior of the system. + function debugLog(msg, data) { + storeVmHookParam(0, msg) + storeVmHookParam(1, data) + setHook(VM_HOOK_DEBUG_LOG()) + } + + /// @dev Triggers a hook that displays the returndata on the server side. + function debugReturndata() { + debugLog("returndataptr", returnDataPtr()) + storeVmHookParam(0, returnDataPtr()) + setHook(VM_HOOK_DEBUG_RETURNDATA()) + } + + /// @dev Triggers a hook that notifies the operator about the factual number of gas + /// refunded to the user. This is to be used by the operator to derive the correct + /// `gasUsed` in the API. + function notifyAboutRefund(refund) { + storeVmHookParam(0, refund) + setHook(VM_NOTIFY_OPERATOR_ABOUT_FINAL_REFUND()) + debugLog("refund(gas)", refund) + } + + function notifyExecutionResult(success) { + let ptr := returnDataPtr() + storeVmHookParam(0, success) + storeVmHookParam(1, ptr) + setHook(VM_HOOK_EXECUTION_RESULT()) + + debugLog("execution result: success", success) + debugLog("execution result: ptr", ptr) + } + + /// @dev Asks operator for the refund for the transaction. The function provides + /// the operator with the proposed refund gas by the bootloader, + /// total spent gas on the pubdata and gas per 1 byte of pubdata. + /// This function is called before the refund stage, because at that point + /// only the operator knows how close does a transaction + /// bring us to closing the batch as well as how much the transaction + /// should've spent on the pubdata/computation/etc. + /// After it is run, the operator should put the expected refund + /// into the memory slot (in the out of circuit execution). + /// Since the slot after the transaction is not touched, + /// this slot can be used in the in-circuit VM out of box. + /// @param proposedRefund The proposed refund gas by the bootloader. + /// @param spentOnPubdata The number of gas that transaction spent on the pubdata. + /// @param gasPerPubdataByte The price of each byte of pubdata in L2 gas. + function askOperatorForRefund( + proposedRefund, + spentOnPubdata, + gasPerPubdataByte + ) { + storeVmHookParam(0, proposedRefund) + storeVmHookParam(1, spentOnPubdata) + storeVmHookParam(2, gasPerPubdataByte) + setHook(VM_HOOK_ASK_OPERATOR_FOR_REFUND()) + } + + /// + /// Error codes used for more correct diagnostics from the server side. + /// + + function ETH_CALL_ERR_CODE() -> ret { + ret := 0 + } + + function ACCOUNT_TX_VALIDATION_ERR_CODE() -> ret { + ret := 1 + } + + function FAILED_TO_CHARGE_FEE_ERR_CODE() -> ret { + ret := 2 + } + + function FROM_IS_NOT_AN_ACCOUNT_ERR_CODE() -> ret { + ret := 3 + } + + function FAILED_TO_CHECK_ACCOUNT_ERR_CODE() -> ret { + ret := 4 + } + + function UNACCEPTABLE_GAS_PRICE_ERR_CODE() -> ret { + ret := 5 + } + + function FAILED_TO_SET_NEW_BATCH_ERR_CODE() -> ret { + ret := 6 + } + + function PAY_FOR_TX_FAILED_ERR_CODE() -> ret { + ret := 7 + } + + function PRE_PAYMASTER_PREPARATION_FAILED_ERR_CODE() -> ret { + ret := 8 + } + + function PAYMASTER_VALIDATION_FAILED_ERR_CODE() -> ret { + ret := 9 + } + + function FAILED_TO_SEND_FEES_TO_THE_OPERATOR() -> ret { + ret := 10 + } + + function UNACCEPTABLE_PUBDATA_PRICE_ERR_CODE() -> ret { + ret := 11 + } + + function TX_VALIDATION_FAILED_ERR_CODE() -> ret { + ret := 12 + } + + function MAX_PRIORITY_FEE_PER_GAS_GREATER_THAN_MAX_FEE_PER_GAS() -> ret { + ret := 13 + } + + function BASE_FEE_GREATER_THAN_MAX_FEE_PER_GAS() -> ret { + ret := 14 + } + + function PAYMASTER_RETURNED_INVALID_CONTEXT() -> ret { + ret := 15 + } + + function PAYMASTER_RETURNED_CONTEXT_IS_TOO_LONG() -> ret { + ret := 16 + } + + function ASSERTION_ERROR() -> ret { + ret := 17 + } + + function FAILED_TO_MARK_FACTORY_DEPS() -> ret { + ret := 18 + } + + function TX_VALIDATION_OUT_OF_GAS() -> ret { + ret := 19 + } + + function NOT_ENOUGH_GAS_PROVIDED_ERR_CODE() -> ret { + ret := 20 + } + + function ACCOUNT_RETURNED_INVALID_MAGIC_ERR_CODE() -> ret { + ret := 21 + } + + function PAYMASTER_RETURNED_INVALID_MAGIC_ERR_CODE() -> ret { + ret := 22 + } + + function MINT_ETHER_FAILED_ERR_CODE() -> ret { + ret := 23 + } + + function FAILED_TO_APPEND_TRANSACTION_TO_L2_BLOCK() -> ret { + ret := 24 + } + + function FAILED_TO_SET_L2_BLOCK() -> ret { + ret := 25 + } + + function FAILED_TO_PUBLISH_TIMESTAMP_DATA_TO_L1() -> ret { + ret := 26 + } + + function L1_MESSENGER_PUBLISHING_FAILED_ERR_CODE() -> ret { + ret := 27 + } + + function L1_MESSENGER_LOG_SENDING_FAILED_ERR_CODE() -> ret { + ret := 28 + } + + function FAILED_TO_CALL_SYSTEM_CONTEXT_ERR_CODE() -> ret { + ret := 29 + } + + /// @dev Accepts a 1-word literal and returns its length in bytes + /// @param str A string literal + function getStrLen(str) -> len { + len := 0 + // The string literals are stored left-aligned. Thus, + // In order to get the length of such string, + // we shift it to the left (remove one byte to the left) until + // no more non-empty bytes are left. + for {} str {str := shl(8, str)} { + len := add(len, 1) + } + } + + // Selector of the errors used by the "require" statements in Solidity + // and the one that can be parsed by our server. + function GENERAL_ERROR_SELECTOR() -> ret { + ret := 0x08c379a000000000000000000000000000000000000000000000000000000000 + } + + /// @notice Reverts with assertion error with the provided error string literal. + function assertionError(err) { + let ptr := 0 + + // The first byte indicates that the revert reason is an assertion error + mstore8(ptr, ASSERTION_ERROR()) + ptr := add(ptr, 1) + + // Then, we need to put the returndata in a way that is easily parsable by our + // servers + mstore(ptr, GENERAL_ERROR_SELECTOR()) + ptr := add(ptr, 4) + + // Then, goes the "data offset". It is has constant value of 32. + mstore(ptr, 32) + ptr := add(ptr, 32) + + // Then, goes the length of the string: + mstore(ptr, getStrLen(err)) + ptr := add(ptr, 32) + + // Then, we put the actual string + mstore(ptr, err) + ptr := add(ptr, 32) + + revert(0, ptr) + } + + /// @notice Accepts an error code and whether there is a need to copy returndata + /// @param errCode The code of the error + /// @param sendReturnData A flag of whether or not the returndata should be used in the + /// revert reason as well. + function revertWithReason(errCode, sendReturnData) { + let returndataLen := 1 + mstore8(0, errCode) + + if sendReturnData { + // Here we ignore all kinds of limits on the returned data, + // since the `revert` will happen shortly after. + returndataLen := add(returndataLen, returndatasize()) + returndatacopy(1, 0, returndatasize()) + } + revert(0, returndataLen) + } + + /// @notice The id of the VM hook that notifies the operator that the transaction + /// validation rules should start applying (i.e. the user should not be allowed to access + /// other users' storage, etc). + function VM_HOOK_ACCOUNT_VALIDATION_ENTERED() -> ret { + ret := 0 + } + + /// @notice The id of the VM hook that notifies the operator that the transaction + /// paymaster validation has started. + function VM_HOOK_PAYMASTER_VALIDATION_ENTERED() -> ret { + ret := 1 + } + + /// @notice The id of the VM hook that notifies the operator that the transaction's validation + /// restrictions should no longer apply. Note, that this is different from the validation ending, + /// since for instance the bootloader needs to do some actions during validation which are forbidden for users. + /// So this hook is used to notify the operator that the restrictions should be temporarily lifted. + function VM_HOOK_NO_VALIDATION_ENTERED() -> ret { + ret := 2 + } + + /// @notice The id of the VM hook that notifies the operator that the transaction's validation has ended. + function VM_HOOK_VALIDATION_STEP_ENDED() -> ret { + ret := 3 + } + + /// @notice The id of the VM hook that notifies the operator that the transaction's execution has started. + function VM_HOOK_TX_HAS_ENDED() -> ret { + ret := 4 + } + + /// @notice The id of the VM hook that is used to emit debugging logs consisting of pair . + function VM_HOOK_DEBUG_LOG() -> ret { + ret := 5 + } + + /// @notice The id of the VM hook that is used to emit debugging logs with the returndata of the latest transaction. + function VM_HOOK_DEBUG_RETURNDATA() -> ret { + ret := 6 + } + + /// @notice The id of the VM hook that is used to notify the operator about the entry into the + /// `ZKSYNC_CATCH_NEAR_CALL` function. + function VM_HOOK_CATCH_NEAR_CALL() -> ret { + ret := 7 + } + + /// @notice The id of the VM hook that is used to notify the operator about the need to put the refund for + /// the current transaction into the bootloader's memory. + function VM_HOOK_ASK_OPERATOR_FOR_REFUND() -> ret { + ret := 8 + } + + /// @notice The id of the VM hook that is used to notify the operator about the refund given to the user by the bootloader. + function VM_NOTIFY_OPERATOR_ABOUT_FINAL_REFUND() -> ret { + ret := 9 + } + + /// @notice The id of the VM hook that is used to notify the operator about the execution result of the transaction. + function VM_HOOK_EXECUTION_RESULT() -> ret { + ret := 10 + } + + /// @notice The id of the VM hook that is used to notify the operator that it needs to insert the information about the last + /// fictive miniblock. + function VM_HOOK_FINAL_L2_STATE_INFO() -> ret { + ret := 11 + } + + /// @norice The id of the VM hook that use used to notify the operator that it needs to insert the pubdata. + function VM_HOOK_PUBDATA_REQUESTED() -> ret { + ret := 12 + } + + // Need to prevent the compiler from optimizing out similar operations, + // which may have different meaning for the offline debugging + function $llvm_NoInline_llvm$_unoptimized(val) -> ret { + ret := add(val, callvalue()) + } + + /// @notice Triggers a VM hook. + /// The server will recognize it and output corresponding logs. + function setHook(hook) { + mstore(VM_HOOK_PTR(), $llvm_NoInline_llvm$_unoptimized(hook)) + } + + /// @notice Sets a value to a param of the vm hook. + /// @param paramId The id of the VmHook parameter. + /// @param value The value of the parameter. + /// @dev This method should be called before triggering the VM hook itself. + /// @dev It is the responsibility of the caller to never provide + /// paramId smaller than the VM_HOOK_PARAMS() + function storeVmHookParam(paramId, value) { + let offset := add(VM_HOOK_PARAMS_OFFSET(), mul(32, paramId)) + mstore(offset, $llvm_NoInline_llvm$_unoptimized(value)) + } + + /// @dev Log key used by Executor.sol for processing. See Constants.sol::SystemLogKey enum + function chainedPriorityTxnHashLogKey() -> ret { + ret := 5 + } + + /// @dev Log key used by Executor.sol for processing. See Constants.sol::SystemLogKey enum + function numberOfLayer1TxsLogKey() -> ret { + ret := 6 + } + + /// @dev Log key used by Executor.sol for processing. See Constants.sol::SystemLogKey enum + function protocolUpgradeTxHashKey() -> ret { + ret := 13 + } + + //////////////////////////////////////////////////////////////////////////// + // Main Transaction Processing + //////////////////////////////////////////////////////////////////////////// + + /// @notice the address that will be the beneficiary of all the fees + let OPERATOR_ADDRESS := mload(0) + + let GAS_PRICE_PER_PUBDATA := 0 + + // Initializing block params + { + /// @notice The hash of the previous batch + let PREV_BATCH_HASH := mload(32) + /// @notice The timestamp of the batch being processed + let NEW_BATCH_TIMESTAMP := mload(64) + /// @notice The number of the new batch being processed. + /// While this number is deterministic for each batch, we + /// still provide it here to ensure consistency between the state + /// of the VM and the state of the operator. + let NEW_BATCH_NUMBER := mload(96) + + /// @notice The minimal price per pubdata byte in ETH that the operator agrees on. + /// In the future, a trustless value will be enforced. + /// For now, this value is trusted to be fairly provided by the operator. + /// It is expected of the operator to already include the L1 batch overhead costs into the value. + let FAIR_PUBDATA_PRICE := mload(128) + + /// @notice The minimal gas price that the operator agrees upon. + /// In the future, it will have an EIP1559-like lower bound. + /// It is expected of the operator to already include the L1 batch overhead costs into the value. + let FAIR_L2_GAS_PRICE := mload(160) + + /// @notice The expected base fee by the operator. + /// Just like the batch number, while calculated on the bootloader side, + /// the operator still provides it to make sure that its data is in sync. + let EXPECTED_BASE_FEE := mload(192) + + validateOperatorProvidedPrices(FAIR_L2_GAS_PRICE, FAIR_PUBDATA_PRICE) + + + + let baseFee := 0 + + baseFee, GAS_PRICE_PER_PUBDATA := getFeeParams( + FAIR_PUBDATA_PRICE, + FAIR_L2_GAS_PRICE + ) + + // Only for the proved batch we enforce that the baseFee proposed + // by the operator is equal to the expected one. For the playground batch, we allow + // the operator to provide any baseFee the operator wants. + if iszero(eq(baseFee, EXPECTED_BASE_FEE)) { + debugLog("baseFee", baseFee) + debugLog("EXPECTED_BASE_FEE", EXPECTED_BASE_FEE) + assertionError("baseFee inconsistent") + } + + upgradeSystemContextIfNeeded() + + setNewBatch(PREV_BATCH_HASH, NEW_BATCH_TIMESTAMP, NEW_BATCH_NUMBER, EXPECTED_BASE_FEE) + + + } + + // Now, we iterate over all transactions, processing each of them + // one by one. + // Here, the `resultPtr` is the pointer to the memory slot, where we will write + // `true` or `false` based on whether the tx execution was successful, + + // The position at which the tx offset of the transaction should be placed + let currentExpectedTxOffset := add(TXS_IN_BATCH_LAST_PTR(), mul(MAX_POSTOP_SLOTS(), 32)) + + let txPtr := TX_DESCRIPTION_BEGIN_BYTE() + + // At the COMPRESSED_BYTECODES_BEGIN_BYTE() the pointer to the newest bytecode to be published + // is stored. + mstore(COMPRESSED_BYTECODES_BEGIN_BYTE(), add(COMPRESSED_BYTECODES_BEGIN_BYTE(), 32)) + + // At start storing keccak256("") as `chainedPriorityTxsHash` and 0 as `numberOfLayer1Txs` + mstore(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), EMPTY_STRING_KECCAK()) + mstore(add(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), 32), 0) + + // Iterating through transaction descriptions + let transactionIndex := 0 + for { + let resultPtr := RESULT_START_PTR() + } lt(txPtr, TXS_IN_BATCH_LAST_PTR()) { + txPtr := add(txPtr, TX_DESCRIPTION_SIZE()) + resultPtr := add(resultPtr, 32) + transactionIndex := add(transactionIndex, 1) + } { + let execute := mload(txPtr) + + debugLog("txPtr", txPtr) + debugLog("execute", execute) + + if iszero(execute) { + // We expect that all transactions that are executed + // are continuous in the array. + break + } + + let txDataOffset := mload(add(txPtr, 32)) + + // We strongly enforce the positions of transactions + if iszero(eq(currentExpectedTxOffset, txDataOffset)) { + debugLog("currentExpectedTxOffset", currentExpectedTxOffset) + debugLog("txDataOffset", txDataOffset) + + assertionError("Tx data offset is incorrect") + } + + currentExpectedTxOffset := validateAbiEncoding(txDataOffset) + + // Checking whether the last slot of the transaction's description + // does not go out of bounds. + if gt(sub(currentExpectedTxOffset, 32), LAST_FREE_SLOT()) { + debugLog("currentExpectedTxOffset", currentExpectedTxOffset) + debugLog("LAST_FREE_SLOT", LAST_FREE_SLOT()) + + assertionError("currentExpectedTxOffset too high") + } + + validateTypedTxStructure(add(txDataOffset, 32)) + + { + debugLog("ethCall", 0) + processTx(txDataOffset, resultPtr, transactionIndex, 0, GAS_PRICE_PER_PUBDATA) + } + // Signal to the vm that the transaction execution is complete + setHook(VM_HOOK_TX_HAS_ENDED()) + // Increment tx index within the system. + considerNewTx() + } + + // Resetting tx.origin and gasPrice to 0, so we don't pay for + // publishing them on-chain. + setTxOrigin(0) + setGasPrice(0) + + // Transferring all the ETH received in the block to the operator + directETHTransfer( + selfbalance(), + OPERATOR_ADDRESS + ) + + // Hook that notifies that the operator should provide final information for the batch + setHook(VM_HOOK_FINAL_L2_STATE_INFO()) + + // Each batch typically ends with a special block which contains no transactions. + // So we need to have this method to reflect it in the system contracts too. + // + // The reason is that as of now our node requires that each storage write (event, etc) belongs to a particular + // L2 block. In case a batch is sealed by timeout (i.e. the resources of the batch have not been exhausted, but we need + // to seal it to assure timely finality), we need to process sending funds to the operator *after* the last + // non-empty L2 block has been already sealed. We can not override old L2 blocks, so we need to create a new empty "fictive" block for it. + // + // The other reason why we need to set this block is so that in case of empty batch (i.e. the one which has no transactions), + // the virtual block number as well as miniblock number are incremented. + setL2Block(transactionIndex) + + callSystemContext(0x3635f3e600000000000000000000000000000000000000000000000000000000) + + publishTimestampDataToL1() + + // Sending system logs (to be processed on L1) + sendToL1Native(true, chainedPriorityTxnHashLogKey(), mload(PRIORITY_TXS_L1_DATA_BEGIN_BYTE())) + sendToL1Native(true, numberOfLayer1TxsLogKey(), mload(add(PRIORITY_TXS_L1_DATA_BEGIN_BYTE(), 32))) + + l1MessengerPublishingCall() + } + } +} diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul.zbin deleted file mode 100644 index 26bb767d1b306421afbad07509e3e3e0b1e68f8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72928 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