From 934d8cd5c228f9de74620b79dd6c77fce252d2e9 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 14:53:35 +0100 Subject: [PATCH 01/32] apply max system contracts address --- contracts/ContractDeployer.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/ContractDeployer.sol b/contracts/ContractDeployer.sol index ed6d3fc2..ae5bd43c 100644 --- a/contracts/ContractDeployer.sol +++ b/contracts/ContractDeployer.sol @@ -44,7 +44,7 @@ contract ContractDeployer is IContractDeployer, ISystemContract { } // It is an EOA, it is still an account. - if (ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0) { + if (_address > address(MAX_SYSTEM_CONTRACT_ADDRESS) && ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0) { return AccountAbstractionVersion.Version1; } From 03ae0fe3dfc460d81ba3088d5f1670a657fa194c Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 14:56:28 +0100 Subject: [PATCH 02/32] add comment --- contracts/ContractDeployer.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/ContractDeployer.sol b/contracts/ContractDeployer.sol index ae5bd43c..74108edf 100644 --- a/contracts/ContractDeployer.sol +++ b/contracts/ContractDeployer.sol @@ -214,6 +214,9 @@ contract ContractDeployer is IContractDeployer, ISystemContract { function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf { _ensureBytecodeIsKnown(_deployment.bytecodeHash); + // Since the `forceDeployOnAddress` function is called only during upgrades, the Governance is trusted to correctly select + // the addresses to deploy the new bytecodes to and to assess whether overriding the AccountInfo for the "force-deployed" + // contract is acceptable. AccountInfo memory newAccountInfo; newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None; // Accounts have sequential nonces by default. From 1ded1471e15d74bafed84a187160485e5dd287dc Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:12:19 +0100 Subject: [PATCH 03/32] Allow only deployments for L1->L2 --- bootloader/bootloader.yul | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 98efc7a6..3d102db7 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -1755,6 +1755,37 @@ object "Bootloader" { } + /// @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(32, mload(add(dataPtr, 32))) + + let isSelectorCreate := or( + eq(selector, {{CREATE_SELECTOR}}) + eq(selector, {{CREATE_ACCOUNT_SELECTOR}}) + ) + let isSelectorCreate2 := or( + eq(selector, {{CREATE2_SELECTOR}}), + eq(selector, {{CREATE2_ACCOUNT_SELECTOR}}) + ) + + // 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 @@ -1764,7 +1795,7 @@ object "Bootloader" { /// 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 := eq(to, CONTRACT_DEPLOYER_ADDR()) + let isSystem := shouldMsgValueMimicCallBeSystem(to, dataPtr) success := mimicCallOnlyResult( MSG_VALUE_SIMULATOR_ADDR(), From dc0aa8f1730288738011a9420e89be6b30118fca Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:37:42 +0100 Subject: [PATCH 04/32] fail to publish timesstamp --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 3d102db7..61e1ad72 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -2546,7 +2546,7 @@ object "Bootloader" { ) if iszero(success) { - debugLog("Failed publish timestamp data to L1", 0) + debugLog("Failed publish timestamp to L1", 0) revertWithReason(FAILED_TO_PUBLISH_TIMESTAMP_DATA_TO_L1(), 1) } } From ff69c5c1fb6a0af6cd0a8a541d17d9b79e5c905f Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:38:21 +0100 Subject: [PATCH 05/32] remove trailing comma --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 61e1ad72..a960008c 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -1435,7 +1435,7 @@ object "Bootloader" { success, // Since the paymaster will be refunded with reservedGas, // it should know about it - safeAdd(gasLeft, reservedGas, "jkl"), + safeAdd(gasLeft, reservedGas, "jkl") )) let gasSpentByPostOp := sub(gasBeforePostOp, gas()) From 4939acc672366305d1b0e24dae93cd5ca2736c4d Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:39:20 +0100 Subject: [PATCH 06/32] correct require for L1Messenger --- contracts/L1Messenger.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/L1Messenger.sol b/contracts/L1Messenger.sol index 5d5b34e6..8df54d3b 100644 --- a/contracts/L1Messenger.sol +++ b/contracts/L1Messenger.sol @@ -195,7 +195,7 @@ contract L1Messenger is IL1Messenger, ISystemContract { /// Check logs uint32 numberOfL2ToL1Logs = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); - require(numberOfL2ToL1Logs <= numberOfL2ToL1Logs, "Too many L2->L1 logs"); + require(numberOfL2ToL1Logs <= L2_TO_L1_LOGS_MERKLE_TREE_LEAVES, "Too many L2->L1 logs"); calldataPtr += 4; bytes32[] memory l2ToL1LogsTreeArray = new bytes32[](L2_TO_L1_LOGS_MERKLE_TREE_LEAVES); From ff73f7c4a7ffd7a479f2d7c4778e27757c03f9de Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:40:16 +0100 Subject: [PATCH 07/32] fix eip1559 --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index a960008c..79095895 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -2933,7 +2933,7 @@ object "Bootloader" { - + assertEq(gt(getFrom(innerTxDataOffset), MAX_SYSTEM_CONTRACT_ADDR()), 1, "from in kernel space") From 015fa52d97d4e09889201d637d59073a017b6b52 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:43:31 +0100 Subject: [PATCH 08/32] charge correctly for the memory overhead --- SystemConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SystemConfig.json b/SystemConfig.json index c88a2304..827e11b5 100644 --- a/SystemConfig.json +++ b/SystemConfig.json @@ -9,7 +9,7 @@ "L1_TX_INTRINSIC_L2_GAS": 167157, "L1_TX_INTRINSIC_PUBDATA": 88, "MAX_GAS_PER_TRANSACTION": 80000000, - "BOOTLOADER_MEMORY_FOR_TXS": 273132, + "BOOTLOADER_MEMORY_FOR_TXS": 8740224, "REFUND_GAS": 7343, "KECCAK_ROUND_COST_GAS": 40, "SHA256_ROUND_COST_GAS": 7, From e05bb21d8e525a8f41789807ecf567c2c819c253 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:46:34 +0100 Subject: [PATCH 09/32] check that we have enough gas for postop --- bootloader/bootloader.yul | 1 + 1 file changed, 1 insertion(+) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 79095895..9edd0e09 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -1425,6 +1425,7 @@ object "Bootloader" { refundRecipient := paymaster if gt(gasLeft, 0) { + checkEnoughGas(gasLeft) let nearCallAbi := getNearCallABI(gasLeft) let gasBeforePostOp := gas() pop(ZKSYNC_NEAR_CALL_callPostOp( From abd65b9f422fb2e4381147845379a4c776d1c6dd Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:51:01 +0100 Subject: [PATCH 10/32] fix comment in L1Messenger --- contracts/L1Messenger.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/L1Messenger.sol b/contracts/L1Messenger.sol index 8df54d3b..7bf67ae4 100644 --- a/contracts/L1Messenger.sol +++ b/contracts/L1Messenger.sol @@ -270,7 +270,7 @@ contract L1Messenger is IL1Messenger, ISystemContract { /// Check State Diffs /// encoding is as follows: - /// header (1 byte version, 2 bytes total len of compressed, 1 byte enumeration index size, 2 bytes number of initial writes) + /// header (1 byte version, 3 bytes total len of compressed, 1 byte enumeration index size, 2 bytes number of initial writes) /// body (N bytes of initial writes [32 byte derived key || compressed value], M bytes repeated writes [enumeration index || compressed value]) /// encoded state diffs: [20bytes address][32bytes key][32bytes derived key][8bytes enum index][32bytes initial value][32bytes final value] require( From 53b6d5835b8164e419e5bc87118997a972036840 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 15:54:27 +0100 Subject: [PATCH 11/32] remove redundant check --- contracts/NonceHolder.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/NonceHolder.sol b/contracts/NonceHolder.sol index f5a08a6b..54d6bc49 100644 --- a/contracts/NonceHolder.sol +++ b/contracts/NonceHolder.sol @@ -132,7 +132,7 @@ contract NonceHolder is INonceHolder, ISystemContract { /// @notice Increments the deployment nonce for the account and returns the previous one. /// @param _address The address of the account which to return the deploy nonce for. /// @return prevDeploymentNonce The deployment nonce at the time this function is called. - function incrementDeploymentNonce(address _address) external onlySystemCall returns (uint256 prevDeploymentNonce) { + function incrementDeploymentNonce(address _address) external returns (uint256 prevDeploymentNonce) { require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), ""); uint256 addressAsKey = uint256(uint160(_address)); uint256 oldRawNonce = rawNonces[addressAsKey]; From 479ab08a73785bbc46e5ee982de84584a17f17fa Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:00:27 +0100 Subject: [PATCH 12/32] safeAdd for refunds --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 9edd0e09..99e8d658 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -920,7 +920,7 @@ object "Bootloader" { refundGas := max(getOperatorRefundForTx(transactionIndex), potentialRefund) } - refundGas := add(refundGas, reservedGas) + refundGas := safeAdd(refundGas, reservedGas) if gt(refundGas, gasLimit) { assertionError("L1: refundGas > gasLimit") From 1f89a6b520d1031bba6cf4745ee4a9f74ee3d0eb Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:11:36 +0100 Subject: [PATCH 13/32] compilation fixes + EOA work correctly on delegatecall --- bootloader/bootloader.yul | 4 ++-- contracts/DefaultAccount.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 99e8d658..089f5694 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -920,7 +920,7 @@ object "Bootloader" { refundGas := max(getOperatorRefundForTx(transactionIndex), potentialRefund) } - refundGas := safeAdd(refundGas, reservedGas) + refundGas := safeAdd(refundGas, reservedGas, "iop") if gt(refundGas, gasLimit) { assertionError("L1: refundGas > gasLimit") @@ -1768,7 +1768,7 @@ object "Bootloader" { let selector := shr(32, mload(add(dataPtr, 32))) let isSelectorCreate := or( - eq(selector, {{CREATE_SELECTOR}}) + eq(selector, {{CREATE_SELECTOR}}), eq(selector, {{CREATE_ACCOUNT_SELECTOR}}) ) let isSelectorCreate2 := or( diff --git a/contracts/DefaultAccount.sol b/contracts/DefaultAccount.sol index 0021839e..cc6a576e 100644 --- a/contracts/DefaultAccount.sol +++ b/contracts/DefaultAccount.sol @@ -219,7 +219,7 @@ contract DefaultAccount is IAccount { _transaction.processPaymasterInput(); } - fallback() external payable { + fallback() external payable ignoreInDelegateCall { // fallback of default account shouldn't be called by bootloader under no circumstances assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS); From 2d479d4c760606426ea2e46cc63aae736d1ba84f Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:14:42 +0100 Subject: [PATCH 14/32] correctly charge for gas overhead --- contracts/L1Messenger.sol | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contracts/L1Messenger.sol b/contracts/L1Messenger.sol index 7bf67ae4..84f3a799 100644 --- a/contracts/L1Messenger.sol +++ b/contracts/L1Messenger.sol @@ -81,8 +81,9 @@ contract L1Messenger is IL1Messenger, ISystemContract { // We need to charge cost of hashing, as it will be used in `publishPubdataAndClearState`: // - keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) and keccakGasCost(64) when reconstructing L2ToL1Log - // - at most 2 times keccakGasCost(64) (as merkle tree can contain ~2*N leaves) - uint256 gasToPay = keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) + 3 * keccakGasCost(64); + // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain + // ~2*N nodes, where the first N nodes are leaves the hash of which is calculated on the previous step). + uint256 gasToPay = keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) + 2 * keccakGasCost(64); SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay)); } @@ -141,12 +142,12 @@ contract L1Messenger is IL1Messenger, ISystemContract { // We need to charge cost of hashing, as it will be used in `publishPubdataAndClearState`: // - keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) and keccakGasCost(64) when reconstructing L2ToL1Log // - keccakGasCost(64) and gasSpentOnMessageHashing when reconstructing Messages - // - at most 2 times keccakGasCost(64) (as merkle tree can contain ~2*N leaves) + // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain + // ~2*N nodes, where the first N nodes are leaves the hash of which is calculated on the previous step). uint256 gasToPay = pubdataLen * gasPerPubdataBytes + keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) + - 4 * - keccakGasCost(64) + + 3 * keccakGasCost(64) + gasSpentOnMessageHashing; SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay)); From 54879153f8d7e3e9226b5b12e44748d011a1ca8f Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:17:00 +0100 Subject: [PATCH 15/32] ensure that upgrade tx always succeeds --- bootloader/bootloader.yul | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 089f5694..57c27f68 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -939,10 +939,14 @@ object "Bootloader" { 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 { From 963c5e05b75b11d6a53a7ddd2e12cd7679ff7977 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:39:32 +0100 Subject: [PATCH 16/32] add force deploy for keccak256 --- contracts/ContractDeployer.sol | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/contracts/ContractDeployer.sol b/contracts/ContractDeployer.sol index 74108edf..c7dc00f0 100644 --- a/contracts/ContractDeployer.sol +++ b/contracts/ContractDeployer.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {ImmutableData} from "./interfaces/IImmutableSimulator.sol"; import {IContractDeployer} from "./interfaces/IContractDeployer.sol"; -import {CREATE2_PREFIX, CREATE_PREFIX, NONCE_HOLDER_SYSTEM_CONTRACT, ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, FORCE_DEPLOYER, MAX_SYSTEM_CONTRACT_ADDRESS, KNOWN_CODE_STORAGE_CONTRACT, ETH_TOKEN_SYSTEM_CONTRACT, IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT, COMPLEX_UPGRADER_CONTRACT} from "./Constants.sol"; +import {CREATE2_PREFIX, CREATE_PREFIX, NONCE_HOLDER_SYSTEM_CONTRACT, ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, FORCE_DEPLOYER, MAX_SYSTEM_CONTRACT_ADDRESS, KNOWN_CODE_STORAGE_CONTRACT, ETH_TOKEN_SYSTEM_CONTRACT, IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT, COMPLEX_UPGRADER_CONTRACT, KECCAK256_SYSTEM_CONTRACT} from "./Constants.sol"; import {Utils} from "./libraries/Utils.sol"; import {EfficientCall} from "./libraries/EfficientCall.sol"; @@ -231,8 +231,23 @@ contract ContractDeployer is IContractDeployer, ISystemContract { false, _deployment.callConstructor ); + } - emit ContractDeployed(_sender, _deployment.bytecodeHash, _deployment.newAddress); + /// @notice The method that is temporarily needed to upgrade the Keccak256 precompile. It is to be removed in the + /// future. Unlike a normal forced deployment, it does not update account information as it requires updating a + /// mapping, and so requires Keccak256 precompile to work already. + /// @dev This method expects the sender (FORCE_DEPLOYER) to provide the correct bytecode hash for the Keccak256 + /// precompile. + function forceDeployKeccak256(bytes32 _keccak256BytecodeHash) external payable onlyCallFrom(FORCE_DEPLOYER) { + _ensureBytecodeIsKnown(_keccak256BytecodeHash); + _constructContract( + msg.sender, + address(KECCAK256_SYSTEM_CONTRACT), + _keccak256BytecodeHash, + msg.data[0:0], + false, + false + ); } /// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses. @@ -298,7 +313,6 @@ contract ContractDeployer is IContractDeployer, ISystemContract { _storeAccountInfo(_newAddress, newAccountInfo); _constructContract(msg.sender, _newAddress, _bytecodeHash, _input, false, true); - emit ContractDeployed(msg.sender, _bytecodeHash, _newAddress); } /// @notice Check that bytecode hash is marked as known on the `KnownCodeStorage` system contracts @@ -355,5 +369,7 @@ contract ContractDeployer is IContractDeployer, ISystemContract { // If we do not call the constructor, we need to set the constructed code hash. ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructedCodeHash(_newAddress, _bytecodeHash); } + + emit ContractDeployed(_sender, _bytecodeHash, _newAddress); } } From 1449d37ba3fb3182b8cc1927b45f786860fe0528 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:40:51 +0100 Subject: [PATCH 17/32] max precompile address fix --- contracts/Constants.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Constants.sol b/contracts/Constants.sol index b6a788a7..45788a11 100644 --- a/contracts/Constants.sol +++ b/contracts/Constants.sol @@ -32,7 +32,7 @@ address constant ECMUL_SYSTEM_CONTRACT = address(0x07); /// 0x01 - ecrecover /// 0x02 - sha256 /// Important! So the constant should be updated if more precompiles are deployed. -uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = uint256(uint160(SHA256_SYSTEM_CONTRACT)); +uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = uint256(uint160(ECMUL_SYSTEM_CONTRACT)); address payable constant BOOTLOADER_FORMAL_ADDRESS = payable(address(SYSTEM_CONTRACTS_OFFSET + 0x01)); IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( From d2103a599ed10ba2315b0f4f67a8103a19242d08 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 16:55:35 +0100 Subject: [PATCH 18/32] correct refund gas for L1 gas --- bootloader/bootloader.yul | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 57c27f68..e892723c 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -917,11 +917,9 @@ object "Bootloader" { // 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) + refundGas := max(getOperatorRefundForTx(transactionIndex), safeAdd(potentialRefund, reservedGas, "iop")) } - refundGas := safeAdd(refundGas, reservedGas, "iop") - if gt(refundGas, gasLimit) { assertionError("L1: refundGas > gasLimit") } From 700f380512eda8b819363d0d93c2187ff6d163bf Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 17:58:25 +0100 Subject: [PATCH 19/32] fix shifting --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index e892723c..240aa72d 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -1767,7 +1767,7 @@ object "Bootloader" { 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(32, mload(add(dataPtr, 32))) + let selector := shr(224, mload(add(dataPtr, 32))) let isSelectorCreate := or( eq(selector, {{CREATE_SELECTOR}}), From 70f6ee4ccf5391089c9ee7cc51926d6eafa0109d Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 18:28:56 +0100 Subject: [PATCH 20/32] correct meta calculation --- contracts/libraries/SystemContractHelper.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/libraries/SystemContractHelper.sol b/contracts/libraries/SystemContractHelper.sol index 8a7734ce..654629c7 100644 --- a/contracts/libraries/SystemContractHelper.sol +++ b/contracts/libraries/SystemContractHelper.sol @@ -270,6 +270,8 @@ library SystemContractHelper { function getZkSyncMeta() internal view returns (ZkSyncMeta memory meta) { uint256 metaPacked = getZkSyncMetaBytes(); meta.gasPerPubdataByte = getGasPerPubdataByteFromMeta(metaPacked); + meta.heapSize = getHeapSizeFromMeta(metaPacked); + meta.auxHeapSize = getAuxHeapSizeFromMeta(metaPacked); meta.shardId = getShardIdFromMeta(metaPacked); meta.callerShardId = getCallerShardIdFromMeta(metaPacked); meta.codeShardId = getCodeShardIdFromMeta(metaPacked); From 28718b1778c0e010fca3e8a3a4805172f5f6adc7 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 18:36:21 +0100 Subject: [PATCH 21/32] nits --- bootloader/bootloader.yul | 10 +++++----- contracts/libraries/Utils.sol | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 240aa72d..357d46a1 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -1180,7 +1180,7 @@ object "Bootloader" { /// @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. - /// @return ergsLeft The ergs left after the validation step. + /// @return gasLeft The gas left after the validation step. function l2TxValidation( txDataOffset, gasLimitForTx, @@ -1232,9 +1232,9 @@ object "Bootloader" { /// @dev The function responsible for the execution step of the L2 transaction. /// @param txDataOffset The offset to the ABI-encoded Transaction struct. - /// @param ergsLeft The ergs left after the validation step. + /// @param gasLeft The gas left after the validation step. /// @return success Whether or not the execution step was successful. - /// @return ergsSpentOnExecute The ergs spent on the transaction execution. + /// @return gasSpentOnExecute The gas spent on the transaction execution. function l2TxExecution( txDataOffset, gasLeft, @@ -1264,7 +1264,7 @@ object "Bootloader" { default { // Note, that since gt(gasLeft, gasSpentOnFactoryDeps) = true // sub(gasLeft, gasSpentOnFactoryDeps) > 0, which is important - // because a nearCall with 0 ergs passes on all the ergs of the parent frame. + // because a nearCall with 0 gas passes on all the gas of the parent frame. gasLeft := sub(gasLeft, gasSpentOnFactoryDeps) let executeABI := getNearCallABI(gasLeft) @@ -1598,7 +1598,7 @@ object "Bootloader" { /// @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 ergs. + /// @param gasPerPubdataByte The price for pubdata byte in gas. /// @param txEncodeLen The length of the ABI-encoding of the transaction function getVerifiedOperatorOverheadForTx( transactionIndex, diff --git a/contracts/libraries/Utils.sol b/contracts/libraries/Utils.sol index 8e66e35f..e56a3f90 100644 --- a/contracts/libraries/Utils.sol +++ b/contracts/libraries/Utils.sol @@ -83,15 +83,15 @@ library Utils { // Note that the length of the bytecode must be provided in 32-byte words. require(_bytecode.length % 32 == 0, "po"); - uint256 bytecodeLenInWords = _bytecode.length / 32; - require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words - require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd + uint256 lengthInWords = _bytecode.length / 32; + require(lengthInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words + require(lengthInWords % 2 == 1, "pr"); // bytecode length in words must be odd hashedBytecode = EfficientCall.sha(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); // Setting the length - hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); + hashedBytecode = hashedBytecode | bytes32(lengthInWords << 224); } } From b48fc2bd2eaa75dae7ec62518342ca7394aab045 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 18:42:09 +0100 Subject: [PATCH 22/32] prev hash --- contracts/SystemContext.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/SystemContext.sol b/contracts/SystemContext.sol index ad20d4bb..1e223773 100644 --- a/contracts/SystemContext.sol +++ b/contracts/SystemContext.sol @@ -382,7 +382,6 @@ contract SystemContext is ISystemContext, ISystemContextDeprecated, ISystemContr // The structure of the "setNewBatch" implies that currentBatchNumber > 0, but we still double check it require(currentBatchNumber > 0, "The current batch number must be greater than 0"); - bytes32 prevBatchHash = batchHash[currentBatchNumber - 1]; // In order to spend less pubdata, the packed version is published uint256 packedTimestamps = (uint256(currentBatchTimestamp) << 128) | currentL2BlockTimestamp; From e5400cf3f867ff91ac6f2d295f41a74553ccd0b0 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 18:46:26 +0100 Subject: [PATCH 23/32] fix some nits --- bootloader/bootloader.yul | 2 +- contracts/precompiles/EcAdd.yul | 2 +- contracts/precompiles/EcMul.yul | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index 357d46a1..e35f2889 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -3289,7 +3289,7 @@ object "Bootloader" { } } - /// @dev Returns the addition of two unsigned integers, reverting on overflow. + /// @dev Returns the subtraction of two unsigned integers, reverting on overflow. function safeSub(x, y, errMsg) -> ret { if gt(y, x) { assertionError(errMsg) diff --git a/contracts/precompiles/EcAdd.yul b/contracts/precompiles/EcAdd.yul index c5581457..bfbac645 100644 --- a/contracts/precompiles/EcAdd.yul +++ b/contracts/precompiles/EcAdd.yul @@ -247,7 +247,7 @@ object "EcAdd" { /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on the Montgomery multiplication. /// @param minuend The minuend in Montgomery form. /// @param subtrahend The subtrahend in Montgomery form. - /// @return ret The result of the Montgomery addition. + /// @return ret The result of the Montgomery subtraction. function montgomerySub(minuend, subtrahend) -> ret { ret := montgomeryAdd(minuend, sub(P(), subtrahend)) } diff --git a/contracts/precompiles/EcMul.yul b/contracts/precompiles/EcMul.yul index 5de5dee0..83c45ff0 100644 --- a/contracts/precompiles/EcMul.yul +++ b/contracts/precompiles/EcMul.yul @@ -225,7 +225,7 @@ object "EcMul" { /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_The_REDC_algorithm for further details on the Montgomery multiplication. /// @param minuend The minuend in Montgomery form. /// @param subtrahend The subtrahend in Montgomery form. - /// @return ret The result of the Montgomery addition. + /// @return ret The result of the Montgomery subtraction. function montgomerySub(minuend, subtrahend) -> ret { ret := montgomeryAdd(minuend, sub(P(), subtrahend)) } From 2027a2c19464ca479d2b311dd1c14d5ba837704d Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Mon, 30 Oct 2023 18:58:29 +0100 Subject: [PATCH 24/32] remove unneeded casting --- contracts/DefaultAccount.sol | 2 -- contracts/SystemContext.sol | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/DefaultAccount.sol b/contracts/DefaultAccount.sol index cc6a576e..d64c2a64 100644 --- a/contracts/DefaultAccount.sol +++ b/contracts/DefaultAccount.sol @@ -101,8 +101,6 @@ contract DefaultAccount is IAccount { if (_isValidSignature(txHash, _transaction.signature)) { magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC; - } else { - magic = bytes4(0); } } diff --git a/contracts/SystemContext.sol b/contracts/SystemContext.sol index 1e223773..bb5fdcfd 100644 --- a/contracts/SystemContext.sol +++ b/contracts/SystemContext.sol @@ -216,14 +216,14 @@ contract SystemContext is ISystemContext, ISystemContextDeprecated, ISystemContr require(_l2BlockNumber > 0, "L2 block number is never expected to be zero"); unchecked { - bytes32 correctPrevBlockHash = _calculateLegacyL2BlockHash(uint128(_l2BlockNumber - 1)); + bytes32 correctPrevBlockHash = _calculateLegacyL2BlockHash(_l2BlockNumber - 1); require(correctPrevBlockHash == _expectedPrevL2BlockHash, "The previous L2 block hash is incorrect"); // Whenever we'll be queried about the hashes of the blocks before the upgrade, // we'll use batches' hashes, so we don't need to store 256 previous hashes. // However, we do need to store the last previous hash in order to be able to correctly calculate the // hash of the new L2 block. - _setL2BlockHash(uint128(_l2BlockNumber - 1), correctPrevBlockHash); + _setL2BlockHash(_l2BlockNumber - 1, correctPrevBlockHash); } } From df46be04379c3760ed5d179683f393f0074b6e49 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 11:52:14 +0100 Subject: [PATCH 25/32] fix lint --- contracts/ContractDeployer.sol | 13 ++++++++----- contracts/L1Messenger.sol | 7 ++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/contracts/ContractDeployer.sol b/contracts/ContractDeployer.sol index c7dc00f0..6674a1ac 100644 --- a/contracts/ContractDeployer.sol +++ b/contracts/ContractDeployer.sol @@ -44,7 +44,10 @@ contract ContractDeployer is IContractDeployer, ISystemContract { } // It is an EOA, it is still an account. - if (_address > address(MAX_SYSTEM_CONTRACT_ADDRESS) && ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0) { + if ( + _address > address(MAX_SYSTEM_CONTRACT_ADDRESS) && + ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0 + ) { return AccountAbstractionVersion.Version1; } @@ -233,11 +236,11 @@ contract ContractDeployer is IContractDeployer, ISystemContract { ); } - /// @notice The method that is temporarily needed to upgrade the Keccak256 precompile. It is to be removed in the - /// future. Unlike a normal forced deployment, it does not update account information as it requires updating a + /// @notice The method that is temporarily needed to upgrade the Keccak256 precompile. It is to be removed in the + /// future. Unlike a normal forced deployment, it does not update account information as it requires updating a /// mapping, and so requires Keccak256 precompile to work already. - /// @dev This method expects the sender (FORCE_DEPLOYER) to provide the correct bytecode hash for the Keccak256 - /// precompile. + /// @dev This method expects the sender (FORCE_DEPLOYER) to provide the correct bytecode hash for the Keccak256 + /// precompile. function forceDeployKeccak256(bytes32 _keccak256BytecodeHash) external payable onlyCallFrom(FORCE_DEPLOYER) { _ensureBytecodeIsKnown(_keccak256BytecodeHash); _constructContract( diff --git a/contracts/L1Messenger.sol b/contracts/L1Messenger.sol index 84f3a799..1d407033 100644 --- a/contracts/L1Messenger.sol +++ b/contracts/L1Messenger.sol @@ -81,7 +81,7 @@ contract L1Messenger is IL1Messenger, ISystemContract { // We need to charge cost of hashing, as it will be used in `publishPubdataAndClearState`: // - keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) and keccakGasCost(64) when reconstructing L2ToL1Log - // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain + // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain // ~2*N nodes, where the first N nodes are leaves the hash of which is calculated on the previous step). uint256 gasToPay = keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) + 2 * keccakGasCost(64); SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay)); @@ -142,12 +142,13 @@ contract L1Messenger is IL1Messenger, ISystemContract { // We need to charge cost of hashing, as it will be used in `publishPubdataAndClearState`: // - keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) and keccakGasCost(64) when reconstructing L2ToL1Log // - keccakGasCost(64) and gasSpentOnMessageHashing when reconstructing Messages - // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain + // - at most 1 time keccakGasCost(64) when building the Merkle tree (as merkle tree can contain // ~2*N nodes, where the first N nodes are leaves the hash of which is calculated on the previous step). uint256 gasToPay = pubdataLen * gasPerPubdataBytes + keccakGasCost(L2_TO_L1_LOG_SERIALIZE_SIZE) + - 3 * keccakGasCost(64) + + 3 * + keccakGasCost(64) + gasSpentOnMessageHashing; SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay)); From 9e3f85054aa1a040e45e4a3320e9551556e2bc57 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 11:53:11 +0100 Subject: [PATCH 26/32] update hashes --- SystemContractsHashes.json | 64 +++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/SystemContractsHashes.json b/SystemContractsHashes.json index efce4f4c..d6af6531 100644 --- a/SystemContractsHashes.json +++ b/SystemContractsHashes.json @@ -3,43 +3,43 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/AccountCodeStorage.sol", - "bytecodeHash": "0x0100009b3cd9a137912ffbd406a1d73eaffbcf40a760f3956fea7e051f0c6101", + "bytecodeHash": "0x0100009b57d7cdcc4f8ca7150714056a3ba2015170016b31345c8e9be7b1bd07", "sourceCodeHash": "0xf56f18d6ccec4a1e083ece9d5dea511b610905b3be42bf81e81e53f8a7028162" }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/BootloaderUtilities.sol", - "bytecodeHash": "0x01000975e811c2ba4a3b28f70426598129f0029feb086714980f9513f59531c7", + "bytecodeHash": "0x0100097545ca2b44abb9a998284944aa7f480a5395dc22f0938f263512165572", "sourceCodeHash": "0xcb8d18786a9dca90524de992e3216f57d89192600c2aa758f071a6a6ae3162c4" }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/ComplexUpgrader.sol", - "bytecodeHash": "0x0100005b2eef785c804dc40ec24b3c2339b11a314fec6eb91db551a2523d6a2b", + "bytecodeHash": "0x0100005bdf8df3533c093ed25a5dc0c9fe0b96c444b9e7a736c45b3ea2a1960d", "sourceCodeHash": "0x02b3234b8aa3dde88cf2cf6c1447512dd953ed355be9ba21c22d48ca6d3eee67" }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/Compressor.sol/Compressor.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/Compressor.sol", - "bytecodeHash": "0x010001b7a7bb988e52b8fca05d82bccf63ea34c6617ebea1765c91e911386756", + "bytecodeHash": "0x010001b777c7dd2dc5783c886ba26ba116e52c12b429508bed85937f8c7fa796", "sourceCodeHash": "0x214a2b123ecdf3b135709d0b6207b3d41d9e8c68a0aa74b88c64fc983382d7b0" }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/ContractDeployer.sol", - "bytecodeHash": "0x010005bb3e1bb343565920b37c6c0d716dcfca45bbdada20a305e80ab60a6916", - "sourceCodeHash": "0xed9088758b3cbc9c450da0ac18e0e11359efe7341219ac1c331a4f5712c2dacb" + "bytecodeHash": "0x01000609f1afb4e3c0c200fd2ea758f453331a72cebc3bc9e25646e6f909741f", + "sourceCodeHash": "0x68ef4e51aff165804fb8b5323145cee131b1241bc7f560b58f6adf55c21487c3" }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/DefaultAccount.sol", - "bytecodeHash": "0x0100065d0ea6130f484f6cd4936f2d5114abc9961328d6acd8b311dd00b94546", - "sourceCodeHash": "0x34aaf3d8fbe90cf35efcfa5d8361de8a97be0a7cb60b9b117cda0dfd78fab6a6" + "bytecodeHash": "0x01000651d5f10f05669c19eb992636468629e13c627c57e62dfbaa9d9b313e54", + "sourceCodeHash": "0x9e2ce7d1980532ec388a1079ef68d1eb4e7cec2cf8b0f750f5c62d558f90eb6e" }, { "contractName": "EmptyContract", @@ -52,50 +52,50 @@ "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/ImmutableSimulator.sol", - "bytecodeHash": "0x01000047b7e40b0e0f7bd7051e20853a49b972c6c0ac16872425067cb3288f08", + "bytecodeHash": "0x01000047491565697b9e25232a0f8a4cd870d1f7ada0756e87870be1bcc16de7", "sourceCodeHash": "0x315e71df564977165decbbbda504fee9d3dd98b6ca1e5dc68572d74bc308b03f" }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/KnownCodesStorage.sol", - "bytecodeHash": "0x0100008b806f904a40cadb94318db1d8a8ae9a579f46ee0b50432e4c221572ee", + "bytecodeHash": "0x0100008bbd2766597fd11f3d30a2a44c204f6fd0c418893bb8baa532d151e63f", "sourceCodeHash": "0x33c7e9af04650d7e802ecfcf099fefde1ddb1a4268f521c0d69dea014ce5853d" }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/L1Messenger.sol", - "bytecodeHash": "0x010002fb863dc09dbfdae276418c307eb39af03f335a0b23a2edc8bcd1835fce", - "sourceCodeHash": "0x1c355d04ecf4e4c39ab6481f2bb17e5e30d3aa4563342aaa4c9aa122ac3a14d3" + "bytecodeHash": "0x01000301a157872ad631b2de1632b7583116275d97a3f9f7319b738a49c80270", + "sourceCodeHash": "0x965ad585680128f4f6be86527967b0733c6bd2361179e61e0f70ac7de8955d5c" }, { "contractName": "L2EthToken", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/L2EthToken.sol/L2EthToken.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/L2EthToken.sol", - "bytecodeHash": "0x01000139b0930df0818b0f10f7c78feed9ca93020efcb72e749a7ea842d08576", + "bytecodeHash": "0x01000139431883ef14d8e0ac63fe7812cc691d9439f822156804df13608f252f", "sourceCodeHash": "0xb8e404a5e82c50b9f0cfb6412049d1174df3fbe8af40750a756ad0c1cfefb593" }, { "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/MsgValueSimulator.sol", - "bytecodeHash": "0x0100006f5dab2685a586d5ebbd360a2c1c2d593df1ab8267d8e172d92a202bfa", + "bytecodeHash": "0x0100006f3f47fe89b9d7ffc1abc4f1d203aa6000cbc6887398f41a470164d6c1", "sourceCodeHash": "0x038cc8e7fe97ad4befa2d5ab4ae77fdefdecc20338142565b8086cd9342868ef" }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/NonceHolder.sol", - "bytecodeHash": "0x0100012f7252eee16af884775bd3279b577bbed64f124349ac6179aeb6ae3cb8", - "sourceCodeHash": "0xdfdd234e9d7f6cc7dfb0b9c8b6a2dea3dc40204539bfb836c9ae2bb1dc9cbb1f" + "bytecodeHash": "0x0100012d09bdadbfaf7cb3b4b9ef06e834854348fa85562d5671f64eae501f51", + "sourceCodeHash": "0x6f6fbe275497e3423b9b3b954b96ee7f7db5f5cd98a2ef1f4d6b4f83cb484234" }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/SystemContext.sol/SystemContext.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/SystemContext.sol", - "bytecodeHash": "0x0100023f1761d12df53e8581fabcb359cb069bbd2a7a7a3ef0b49f2f5d46169a", - "sourceCodeHash": "0x60d9007efb7f1bf9417f0856f3799937357a64c2e5f858d13d3ee584e8b9832e" + "bytecodeHash": "0x0100023b228f6ad41173bffaf14a9b58f22d3cd1e7d3de6c081a9d16d2f1bd27", + "sourceCodeHash": "0xf5552ba7b356e3fdcc55af6510fbdba9cec77f66a8ec9930a657d5aea5add4fb" }, { "contractName": "EventWriter", @@ -108,15 +108,15 @@ "contractName": "EcAdd", "bytecodePath": "contracts/precompiles/artifacts/EcAdd.yul/EcAdd.yul.zbin", "sourceCodePath": "contracts/precompiles/EcAdd.yul", - "bytecodeHash": "0x010000c56c054a0de4a36b133d3c114ec514c3ce0334ad7759c202392386a913", - "sourceCodeHash": "0xe73c8960a8b4060113adca9f03207d379580d172df9f0b499dd5353934a557a6" + "bytecodeHash": "0x010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d433", + "sourceCodeHash": "0x32645126b8765e4f7ced63c9508c70edc4ab734843d5f0f0f01d153c27206cee" }, { "contractName": "EcMul", "bytecodePath": "contracts/precompiles/artifacts/EcMul.yul/EcMul.yul.zbin", "sourceCodePath": "contracts/precompiles/EcMul.yul", - "bytecodeHash": "0x010001378d31273c8e58caa12bcf1a5694e66a0aefdba2504adb8e3eb02b21c7", - "sourceCodeHash": "0x6c4b11542bcf85e6e02ca193fc0548353b1f21c27e972b9e73781e8f7eaf50b0" + "bytecodeHash": "0x0100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba571350675", + "sourceCodeHash": "0xdad8be6e926155a362ea05b132ba8b6c634e978a41f79bb6390b870e18049e45" }, { "contractName": "Ecrecover", @@ -143,35 +143,35 @@ "contractName": "bootloader_test", "bytecodePath": "bootloader/build/artifacts/bootloader_test.yul/bootloader_test.yul.zbin", "sourceCodePath": "bootloader/build/bootloader_test.yul", - "bytecodeHash": "0x0100037b0462ed355364eaabccbea2a018afad4c8841b9856514c027400f1b10", - "sourceCodeHash": "0x467a36057882d6740a016cda812798d1be9a0ea60cb7ef90996e2c5be55e75a4" + "bytecodeHash": "0x010003852143f6658ecc92ebd784759a34b444f01bc78643e1bd4b0c479157e1", + "sourceCodeHash": "0x141bbebe33792f75d7727e5a19a928e38f14b4da7ccf21e582b9a6a8ba4017bb" }, { "contractName": "fee_estimate", "bytecodePath": "bootloader/build/artifacts/fee_estimate.yul/fee_estimate.yul.zbin", "sourceCodePath": "bootloader/build/fee_estimate.yul", - "bytecodeHash": "0x010009434283c0bc9f32e51a9aa84523ee7a381e3e0c5ae63f639998d915f54b", - "sourceCodeHash": "0x3fb415ac6f59c35ea17b85aabb551df1b44a6fc7e051c2e33f5fc76c17432167" + "bytecodeHash": "0x0100096b643f53e2d82883d76899b9d2fa36d73eb4462b7f4cc8623c238f4b5e", + "sourceCodeHash": "0x576a59bca03442bea891eadff82ef6515844fe9cea143928c2101999c9353a67" }, { "contractName": "gas_test", "bytecodePath": "bootloader/build/artifacts/gas_test.yul/gas_test.yul.zbin", "sourceCodePath": "bootloader/build/gas_test.yul", - "bytecodeHash": "0x01000927ea81a1afe5a586853a9c43fb928bcf1f1fba51a19c48ce1b940867c7", - "sourceCodeHash": "0x84648c958714d952248b8553456b5a5e3860e00871f01644297531e991a67d64" + "bytecodeHash": "0x0100094be8b6434783b9132dc59c8a55810e30c689caf4b97f011ba5484a8071", + "sourceCodeHash": "0xfb030709056ad8876e46d24d740db4616d023da662c447596d9fbfd7b2f37ec0" }, { "contractName": "playground_batch", "bytecodePath": "bootloader/build/artifacts/playground_batch.yul/playground_batch.yul.zbin", "sourceCodePath": "bootloader/build/playground_batch.yul", - "bytecodeHash": "0x0100094d801bf4180d020692a95cf26a3c9adcaedfd5be47ec08b1637b0282da", - "sourceCodeHash": "0xe02bed16015da2f03dcf5a7ed1bf2132009e69f4bfb5335e13cc406327e84d5e" + "bytecodeHash": "0x010009753bfed17c34977063d4c3c54a6305db6be5416908183e5732cc447c18", + "sourceCodeHash": "0x28328cbd84864be273fd975d3e2419b3e81216d94a1468f55f7a8673b98a45f9" }, { "contractName": "proved_batch", "bytecodePath": "bootloader/build/artifacts/proved_batch.yul/proved_batch.yul.zbin", "sourceCodePath": "bootloader/build/proved_batch.yul", - "bytecodeHash": "0x010009411d9c2342671c57d5ce038ce3e142c750df85ac5d23f67b4e4215fede", - "sourceCodeHash": "0xd48e5abbfbb493eacfcbe6dc788eada867d58ab8596d55736b496b1c2e22c636" + "bytecodeHash": "0x0100096558918a9a6b1bc9b1e7fad3c8eac481379e7b2a45b72328543c7c6c29", + "sourceCodeHash": "0x18ea644d20160058fd21424c212c7adf37c98be61996bddf39ba47f9506ad21a" } ] From 9d2f40c958881d1e4d038b83362535a0af05c89c Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 12:31:38 +0100 Subject: [PATCH 27/32] update hashes --- SystemContractsHashes.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SystemContractsHashes.json b/SystemContractsHashes.json index d6af6531..f051dcb7 100644 --- a/SystemContractsHashes.json +++ b/SystemContractsHashes.json @@ -31,8 +31,8 @@ "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/ContractDeployer.sol", - "bytecodeHash": "0x01000609f1afb4e3c0c200fd2ea758f453331a72cebc3bc9e25646e6f909741f", - "sourceCodeHash": "0x68ef4e51aff165804fb8b5323145cee131b1241bc7f560b58f6adf55c21487c3" + "bytecodeHash": "0x010006098193e41685f5e63b2d0e7369be71b39f41ccd13296a03801645fb1df", + "sourceCodeHash": "0x86a2b81dabf56644a7d183c7a8ceb58ce5a2417f89af9eed2491c44943b7cd0a" }, { "contractName": "DefaultAccount", @@ -66,8 +66,8 @@ "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/cache-zk/solpp-generated-contracts/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "cache-zk/solpp-generated-contracts/L1Messenger.sol", - "bytecodeHash": "0x01000301a157872ad631b2de1632b7583116275d97a3f9f7319b738a49c80270", - "sourceCodeHash": "0x965ad585680128f4f6be86527967b0733c6bd2361179e61e0f70ac7de8955d5c" + "bytecodeHash": "0x010003010b1831cbe1c9eff5407f46cdafef9ead24e5b10571ea681cd04b044a", + "sourceCodeHash": "0xc6d60bfcea55041604cf3714bf4d9e0af51737953e1578430f574f800e4292d7" }, { "contractName": "L2EthToken", From 59b26b3e2ce8ce69295b045a6f26e0c40c08b797 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Tue, 31 Oct 2023 12:37:05 +0100 Subject: [PATCH 28/32] Update bootloader/bootloader.yul Co-authored-by: Vlad Bochok <41153528+vladbochok@users.noreply.github.com> --- bootloader/bootloader.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/bootloader.yul b/bootloader/bootloader.yul index e35f2889..5f25cbfb 100644 --- a/bootloader/bootloader.yul +++ b/bootloader/bootloader.yul @@ -3289,7 +3289,7 @@ object "Bootloader" { } } - /// @dev Returns the subtraction of two unsigned integers, reverting on overflow. + /// @dev Returns the subtraction of two unsigned integers, reverting on underflow. function safeSub(x, y, errMsg) -> ret { if gt(y, x) { assertionError(errMsg) From 50957fd0dd1171bc421246d9bab74f86efbf3498 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 13:12:21 +0100 Subject: [PATCH 29/32] update max precompile address constant --- contracts/Constants.sol | 10 ++++------ test/AccountCodeStorage.spec.ts | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/contracts/Constants.sol b/contracts/Constants.sol index 45788a11..b723ea39 100644 --- a/contracts/Constants.sol +++ b/contracts/Constants.sol @@ -27,12 +27,10 @@ address constant SHA256_SYSTEM_CONTRACT = address(0x02); address constant ECADD_SYSTEM_CONTRACT = address(0x06); address constant ECMUL_SYSTEM_CONTRACT = address(0x07); -/// @dev The current maximum deployed precompile address. -/// Note: currently only two precompiles are deployed: -/// 0x01 - ecrecover -/// 0x02 - sha256 -/// Important! So the constant should be updated if more precompiles are deployed. -uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = uint256(uint160(ECMUL_SYSTEM_CONTRACT)); +/// @dev The maximal possible address of an L1-like precompie. These precompiles maintain the following properties: +/// - Their extcodehash is EMPTY_STRING_KECCAK +/// - Their extcodesize is 0 despite having a bytecode formally deployed there. +uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = 0xff; address payable constant BOOTLOADER_FORMAL_ADDRESS = payable(address(SYSTEM_CONTRACTS_OFFSET + 0x01)); IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( diff --git a/test/AccountCodeStorage.spec.ts b/test/AccountCodeStorage.spec.ts index 9bed917b..658f2bff 100644 --- a/test/AccountCodeStorage.spec.ts +++ b/test/AccountCodeStorage.spec.ts @@ -142,9 +142,15 @@ describe('AccountCodeStorage tests', function () { describe('getCodeHash', function () { it('precompile', async () => { + // Check that the smallest precompile has EMPTY_STRING_KECCAK hash expect(await accountCodeStorage.getCodeHash('0x0000000000000000000000000000000000000001')).to.be.eq( EMPTY_STRING_KECCAK ); + + // Check that the upper end of the precompile range has EMPTY_STRING_KECCAK hash + expect(await accountCodeStorage.getCodeHash('0x00000000000000000000000000000000000000ff')).to.be.eq( + EMPTY_STRING_KECCAK + ); }); it('EOA with non-zero nonce', async () => { From 9ab3e8cedf7fe6268bdd6a6f0c0049a53777dc55 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 13:13:36 +0100 Subject: [PATCH 30/32] Only the deployer can increment the deployment nonce --- contracts/NonceHolder.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/NonceHolder.sol b/contracts/NonceHolder.sol index 54d6bc49..8b2cf390 100644 --- a/contracts/NonceHolder.sol +++ b/contracts/NonceHolder.sol @@ -133,7 +133,7 @@ contract NonceHolder is INonceHolder, ISystemContract { /// @param _address The address of the account which to return the deploy nonce for. /// @return prevDeploymentNonce The deployment nonce at the time this function is called. function incrementDeploymentNonce(address _address) external returns (uint256 prevDeploymentNonce) { - require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), ""); + require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Only the contract deployer can increment the deployment nonce"); uint256 addressAsKey = uint256(uint160(_address)); uint256 oldRawNonce = rawNonces[addressAsKey]; From 7108391e0df9499f1c0c29376c142539f8ff41c8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 13:15:58 +0100 Subject: [PATCH 31/32] fix lint --- contracts/NonceHolder.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/NonceHolder.sol b/contracts/NonceHolder.sol index 8b2cf390..77f3d88e 100644 --- a/contracts/NonceHolder.sol +++ b/contracts/NonceHolder.sol @@ -133,7 +133,10 @@ contract NonceHolder is INonceHolder, ISystemContract { /// @param _address The address of the account which to return the deploy nonce for. /// @return prevDeploymentNonce The deployment nonce at the time this function is called. function incrementDeploymentNonce(address _address) external returns (uint256 prevDeploymentNonce) { - require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Only the contract deployer can increment the deployment nonce"); + require( + msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), + "Only the contract deployer can increment the deployment nonce" + ); uint256 addressAsKey = uint256(uint160(_address)); uint256 oldRawNonce = rawNonces[addressAsKey]; From b2fc4c4fc3be1654ab58c9e1cbae4623b56e6c3b Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 31 Oct 2023 16:49:38 +0100 Subject: [PATCH 32/32] add some tests --- contracts/test-contracts/DelegateCaller.sol | 20 +++++++++++++ test/ContractDeployer.spec.ts | 9 ++++++ test/DefaultAccount.spec.ts | 33 +++++++++++++++++++-- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 contracts/test-contracts/DelegateCaller.sol diff --git a/contracts/test-contracts/DelegateCaller.sol b/contracts/test-contracts/DelegateCaller.sol new file mode 100644 index 00000000..caa5aae6 --- /dev/null +++ b/contracts/test-contracts/DelegateCaller.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract DelegateCaller { + function delegateCall(address _to) external payable { + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), _to, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} diff --git a/test/ContractDeployer.spec.ts b/test/ContractDeployer.spec.ts index d7ec83b5..18663a48 100644 --- a/test/ContractDeployer.spec.ts +++ b/test/ContractDeployer.spec.ts @@ -30,6 +30,7 @@ describe('ContractDeployer tests', function () { const RANDOM_ADDRESS = ethers.utils.getAddress('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbee1'); const RANDOM_ADDRESS_2 = ethers.utils.getAddress('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbee2'); const RANDOM_ADDRESS_3 = ethers.utils.getAddress('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbee3'); + const EMPTY_KERNEL_ADDRESS = ethers.utils.getAddress('0x0000000000000000000000000000000000000101'); const AA_VERSION_NONE = 0; const AA_VERSION_1 = 1; const NONCE_ORDERING_SEQUENTIAL = 0; @@ -166,6 +167,14 @@ describe('ContractDeployer tests', function () { expect(await contractDeployer.extendedAccountVersion(EOA)).to.be.eq(AA_VERSION_1); }); + it('Empty address', async () => { + // Double checking that the address is indeed empty + expect(await wallet.provider.getCode(EMPTY_KERNEL_ADDRESS)).to.be.eq('0x'); + + // Now testing that the system contracts with empty bytecode are still treated as AA_VERSION_NONE + expect(await contractDeployer.extendedAccountVersion(EMPTY_KERNEL_ADDRESS)).to.be.eq(AA_VERSION_NONE); + }); + it('not AA', async () => { expect(await contractDeployer.extendedAccountVersion(contractDeployerSystemCall.address)).to.be.eq( AA_VERSION_NONE diff --git a/test/DefaultAccount.spec.ts b/test/DefaultAccount.spec.ts index a2460581..08dcd517 100644 --- a/test/DefaultAccount.spec.ts +++ b/test/DefaultAccount.spec.ts @@ -7,6 +7,7 @@ import { Callable, DefaultAccount, DefaultAccount__factory, + DelegateCaller, L2EthToken, L2EthToken__factory, MockERC20Approve, @@ -31,6 +32,7 @@ describe('DefaultAccount tests', function () { let callable: Callable; let mockERC20Approve: MockERC20Approve; let paymasterFlowInterface: ethers.utils.Interface; + let delegateCaller: DelegateCaller; const RANDOM_ADDRESS = ethers.utils.getAddress('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef'); @@ -43,6 +45,7 @@ describe('DefaultAccount tests', function () { nonceHolder = NonceHolder__factory.connect(NONCE_HOLDER_SYSTEM_CONTRACT_ADDRESS, wallet); l2EthToken = L2EthToken__factory.connect(ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, wallet); callable = (await deployContract('Callable')) as Callable; + delegateCaller = (await deployContract('DelegateCaller')) as DelegateCaller; mockERC20Approve = (await deployContract('MockERC20Approve')) as MockERC20Approve; const paymasterFlowInterfaceArtifact = await loadArtifact('IPaymasterFlow'); @@ -352,7 +355,7 @@ describe('DefaultAccount tests', function () { }); describe('fallback/receive', function () { - it('zero value', async () => { + it('zero value by EOA wallet', async () => { const call = { from: wallet.address, to: defaultAccount.address, @@ -362,7 +365,7 @@ describe('DefaultAccount tests', function () { expect(await wallet.provider.call(call)).to.be.eq('0x'); }); - it('non-zero value', async () => { + it('non-zero value by EOA wallet', async () => { const call = { from: wallet.address, to: defaultAccount.address, @@ -371,5 +374,31 @@ describe('DefaultAccount tests', function () { }; expect(await wallet.provider.call(call)).to.be.eq('0x'); }); + + it('zero value by bootloader', async () => { + // Here we need to ensure that during delegatecalls even if `msg.sender` is the bootloader, + // the fallback is behaving correctly + const calldata = delegateCaller.interface.encodeFunctionData('delegateCall', [defaultAccount.address]); + const call = { + from: BOOTLOADER_FORMAL_ADDRESS, + to: delegateCaller.address, + value: 0, + data: calldata + }; + expect(await bootloader.call(call)).to.be.eq('0x'); + }); + + it('non-zero value by bootloader', async () => { + // Here we need to ensure that during delegatecalls even if `msg.sender` is the bootloader, + // the fallback is behaving correctly + const calldata = delegateCaller.interface.encodeFunctionData('delegateCall', [defaultAccount.address]); + const call = { + from: BOOTLOADER_FORMAL_ADDRESS, + to: delegateCaller.address, + value: 3223, + data: calldata + }; + expect(await bootloader.call(call)).to.be.eq('0x'); + }); }); });